Compare commits

...

31 Commits
v1.5 ... v1.3.1

Author SHA1 Message Date
Michael Zillgith edc093e810 - updated CHANGELOG file
- make iec61850_9_2_LE_example to compile with Visual Studio
7 years ago
Michael Zillgith 31f92b5cf0 - .NET API: Added method MmsConnection.ReadMultipleVariables
- .NET API: extended MmsValue.ToString method to print arrays and data access errors
7 years ago
Michael Zillgith 8d728b3ab4 - fixed problem in GOOSE publisher payload length calculation 7 years ago
Michael Zillgith 0d7a3750e4 - IEC 61850 client: implemented tissue 1178 client side (select-response+ is non-NULL) 7 years ago
Michael Zillgith 074f7a8cd1 - Ethernet HAL Linux: limited interface name in Ethernet_getIntefaceMACAddress to prevent #83 7 years ago
Michael Zillgith 3ad71c7a0b - added error handling in iec61850_client_example4 7 years ago
Michael Zillgith 1fbadffd6e - added new functions to windows def files 7 years ago
Michael Zillgith 2bbdfacfe4 - SV: fixed RefrTm and SmpSynch handling in SV publisher 7 years ago
Michael Zillgith 57eb6bd455 - GOOSE publisher: fixed bug in GOOSE message calculation 7 years ago
Michael Zillgith 74a9eb5856 - IEC 61850 client: ControlObjectClient - avoid crash when "ctlVal" is not present in "Oper", also accept "setMag" instead of "ctlVal". 7 years ago
Michael Zillgith 9b4a06e573 - .NET API: ReportControlBlock.GetOwner returns null when no owner available (#79) 7 years ago
Michael Zillgith 4a135fa252 - IEC 61850 server: fixed potential deadlock in report processing 7 years ago
Michael Zillgith f68e97b7a1
Merge pull request #80 from aaribaud/python3-bindings-fix
Fixed Python 3 bindings support
7 years ago
Albert ARIBAUD 8f523b1706 Fixed Python 3 bindings support
1. Allow specifying Python interpreter version with BUILD_PYTHON_VERSION

2. Fix Python modules install path generation (would produce spurious
   line feed which would cause destination directory to be
   /usr/lib/python*/dist-packages\n -- note the final \n!
7 years ago
Michael Zillgith c9fe9b4286 - adopted new functions to version 1.3 7 years ago
Michael Zillgith 1a9de24884 - increased version number to 1.3.1 7 years ago
Michael Zillgith 59e2d19acc - IEC 61850 client: improved support for segmented report handling 7 years ago
Michael Zillgith cd81f9b13d - .NET API: Added ReportControlBlock.GetOwner method (#79) 7 years ago
Michael Zillgith e6ace3a787 - Java SCL parser: added support for timestamp values in "Val" elements 7 years ago
Michael Zillgith 9f0316e4c0 - fixed bug in cmake file (winpcap support) - (#78) 7 years ago
Michael Zillgith ed13cd7ab8 - added C# example code for client side setting group handling 7 years ago
Michael Zillgith 3878a9aad3 - .NET API: added some additional wrapper code for MmsVariableSpecification functions 7 years ago
Michael Zillgith 4d8a768ec5 - .NET API: fixed wrong defintion for ClientReportControlBlock_getResvTms in ReportControlBlock class 7 years ago
Michael Zillgith af0b2f9edb - .NET API: Added missing access functions for ResvTms to ReportControlBlock class (#74) 7 years ago
Michael Zillgith 2a19b2592f - python wrapper: some changes in cmake file 7 years ago
Michael Zillgith eb5b8fba5c
Merge pull request #72 from cedricboudinet/master
python wrapper: correcting version detection for swig
7 years ago
Michael Zillgith c38a0c3d59
Merge pull request #73 from cngkaygusuz/v1.3
Add .gitignore, ignore .build/
7 years ago
Cengiz Kaygusuz f8486de415 Add .gitignore, ignore build/ 7 years ago
Cedric Boudinet c97cac5ad9 correcting version detection for swig 7 years ago
Michael Zillgith 1a8558dcda - Windows Ethernet Hal: fixed memory leak 7 years ago
Michael Zillgith 75ca279489 - Java tools: fixed bug in SCL parser (wrong interpretation of numbers with leading "0" 7 years ago

1
.gitignore vendored

@ -0,0 +1 @@
build/

@ -1,3 +1,17 @@
Changes to version 1.3.1
------------------------
- GOOSE publisher: fixed problem in payload length calculation
- .NET API: Added method MmsConnection.ReadMultipleVariables
- IEC 61850 client: implemented tissue 1178 client side (select-response+ is
non-NULL)
- SV publisher: fixed RefrTm and SmpSynch handling
- IEC 61850 client: improved support for handling segmented reports
- .NET API: Added some additional access function to ReportControlBlock
- Java SCL parser: added support for timestamp values in "Val" elements
- fixed bug in cmake file (winpcap support)
- added C# example code for client side setting group handling
- .NET API: added some additional wrapper code for MmsVariableSpecification functions
Changes to version 1.3.0
------------------------
- IEC 61850 server: more features configurable at runtime
@ -17,7 +31,6 @@ Changes to version 1.3.0
- MMS server: fixed bug in delete variable list service - scope of delete was not considered optional
- some more small bug fixes and optimizations
Changes to version 1.2.2
------------------------

@ -12,7 +12,7 @@ ENABLE_TESTING()
set(LIB_VERSION_MAJOR "1")
set(LIB_VERSION_MINOR "3")
set(LIB_VERSION_PATCH "0")
set(LIB_VERSION_PATCH "1")
set(LIB_VERSION "${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")

@ -28,10 +28,6 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />

@ -48,6 +48,9 @@ namespace IEC61850
public string revision;
}
/// <summary>
/// Represents an MmsConnection object (a single connection to an MMS server)
/// </summary>
public class MmsConnection
{
@ -69,6 +72,25 @@ namespace IEC61850
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
private static extern Int32 MmsConnection_getLocalDetail (IntPtr self);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
private static extern IntPtr MmsConnection_readMultipleVariables(IntPtr self, out int mmsError,
string domainId, IntPtr items);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void MmsValue_delete (IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void LinkedListValueDeleteFunction(IntPtr pointer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void LinkedList_destroyDeep(IntPtr list, LinkedListValueDeleteFunction valueDeleteFunction);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr LinkedList_create ();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void LinkedList_add (IntPtr self, IntPtr data);
private IntPtr self = IntPtr.Zero;
private bool selfDestroy = false;
@ -90,6 +112,15 @@ namespace IEC61850
MmsConnection_destroy(self);
}
private void FreeHGlobaleDeleteFunction (IntPtr pointer)
{
Marshal.FreeHGlobal(pointer);
}
/// <summary>
/// Requests the server identity information
/// </summary>
/// <returns>The server identity.</returns>
public MmsServerIdentity GetServerIdentity ()
{
int mmsError;
@ -100,6 +131,12 @@ namespace IEC61850
IntPtr identity = MmsConnection_identify(self, out mmsError);
if (mmsError != 0)
throw new IedConnectionException("Failed to read server identity", mmsError);
if (identity == IntPtr.Zero)
throw new IedConnectionException("Failed to read server identity");
MmsServerIdentity serverIdentity = (MmsServerIdentity)
Marshal.PtrToStructure(identity, typeof(MmsServerIdentity));
@ -108,14 +145,54 @@ namespace IEC61850
return serverIdentity;
}
/// <summary>
/// Sets the local detail (maximum MMS PDU size)
/// </summary>
/// <param name="localDetail">maximum accepted MMS PDU size in bytes</param>
public void SetLocalDetail(int localDetail) {
MmsConnection_setLocalDetail (self, localDetail);
}
/// <summary>
/// Gets the local detail (maximum MMS PDU size)
/// </summary>
/// <returns>maximum accepted MMS PDU size in bytes</returns>
public int GetLocalDetail() {
return MmsConnection_getLocalDetail (self);
}
/// <summary>
/// Reads multipe MMS variables from the same domain
/// </summary>
/// <returns>MmsValue of type MMS_ARRAY containing the access results for the requested variables.</returns>
/// <param name="domainName">the domain name (logical device)</param>
/// <param name="variables">list of variable names (in MMS notation e.g. "GGIO$ST$Ind1")</param>
public MmsValue ReadMultipleVariables(string domainName, List<string> variables)
{
IntPtr linkedList = LinkedList_create ();
foreach (string variableName in variables) {
IntPtr handle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi (variableName);
LinkedList_add (linkedList, handle);
}
int error;
IntPtr mmsValue = MmsConnection_readMultipleVariables(self, out error, domainName, linkedList);
LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
if (error != 0)
{
if (mmsValue != IntPtr.Zero)
MmsValue_delete(mmsValue);
throw new IedConnectionException("ReadMultipleVariables failed", error);
}
return new MmsValue(mmsValue, true);
}
}

@ -179,12 +179,14 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_setElement(IntPtr complexValue, int index, IntPtr elementValue);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getChildValue(IntPtr self, IntPtr value, string childId);
internal IntPtr valueReference; /* reference to native MmsValue instance */
private bool responsableForDeletion; /* if .NET wrapper is responsable for the deletion of the native MmsValue instance */
// TODO make internal
public MmsValue (IntPtr value)
internal MmsValue (IntPtr value)
{
valueReference = value;
this.responsableForDeletion = false;
@ -864,6 +866,28 @@ namespace IEC61850
throw new MmsValueException ("Value type is not float");
}
/// <summary>
/// Gets the child value with the given name
/// </summary>
/// <returns>the child value or null if no matching child has been found.</returns>
/// <param name="childPath">path specifying the child using '.' or '$' as path element separator.</param>
/// <param name="specification">the variable specification to use.</param>
public MmsValue GetChildValue(string childPath, MmsVariableSpecification specification)
{
StringBuilder childPathStr = new StringBuilder(childPath);
childPathStr.Replace('.', '$');
IntPtr childPtr = MmsVariableSpecification_getChildValue(specification.self, valueReference, childPathStr.ToString());
if (childPtr == IntPtr.Zero)
return null;
else
{
return new MmsValue(childPtr);
}
}
public override bool Equals (object obj)
{
MmsValue otherValue = (MmsValue) obj;
@ -914,9 +938,31 @@ namespace IEC61850
return retString;
}
case MmsType.MMS_ARRAY:
{
string retString = "[";
bool first = true;
foreach (MmsValue element in this) {
if (first) {
retString += element.ToString ();
first = false;
} else {
retString += ", " + element.ToString ();
}
}
retString += "]";
return retString;
}
case MmsType.MMS_DATA_ACCESS_ERROR:
return "error: " + GetDataAccessError().ToString();
default:
return "unknown";
return "unknown (type:" + GetType().ToString() + ")";
}
}

@ -26,6 +26,7 @@ using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Collections;
using System.Text;
namespace IEC61850
{
@ -39,9 +40,6 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsVariableSpecification_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getChildValue(IntPtr self, IntPtr value, string childId);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsVariableSpecification_getNamedVariableRecursive(IntPtr variable, string nameId);
@ -63,13 +61,20 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsVariableSpecification_getExponentWidth(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool MmsVariableSpecification_isValueOfType(IntPtr self, IntPtr value);
internal IntPtr self;
private bool responsableForDeletion;
internal MmsVariableSpecification (IntPtr self)
/* only to prevent garbage collector to destroy parent element */
internal MmsVariableSpecification parent = null;
internal MmsVariableSpecification (IntPtr self, MmsVariableSpecification parent)
{
this.self = self;
this.responsableForDeletion = false;
this.parent = parent;
}
internal MmsVariableSpecification (IntPtr self, bool responsableForDeletion)
@ -78,6 +83,27 @@ namespace IEC61850
this.responsableForDeletion = responsableForDeletion;
}
/// <summary>
/// Get a child variable specification by its name
/// </summary>
/// <returns>the varibable specification of the child, or null if no such child is existing.</returns>
/// <param name="name">The child name (can also be a path separating the elements with '.' or '$')</param>
public MmsVariableSpecification GetChildByName(string name)
{
StringBuilder nameId = new StringBuilder(name);
nameId.Replace('.', '$');
IntPtr varSpecPtr = MmsVariableSpecification_getNamedVariableRecursive(self, nameId.ToString());
if (varSpecPtr != IntPtr.Zero)
{
return new MmsVariableSpecification(varSpecPtr, this);
}
else
return null;
}
~MmsVariableSpecification ()
{
if (responsableForDeletion)
@ -106,7 +132,7 @@ namespace IEC61850
{
if (GetType() == MmsType.MMS_ARRAY) {
IntPtr varSpecPtr = MmsVariableSpecification.MmsVariableSpecification_getArrayElementSpecification(self);
return new MmsVariableSpecification(varSpecPtr);
return new MmsVariableSpecification(varSpecPtr, this);
}
else
throw new MmsValueException ("specification is of wrong type");
@ -127,7 +153,7 @@ namespace IEC61850
if ((index >= 0) && (index < Size ())) {
IntPtr varSpecPtr = MmsVariableSpecification_getChildSpecificationByIndex(self, index);
return new MmsVariableSpecification(varSpecPtr);
return new MmsVariableSpecification(varSpecPtr, this);
}
else
throw new MmsValueException ("Index out of bounds");
@ -157,6 +183,16 @@ namespace IEC61850
return MmsVariableSpecification_getSize(self);
}
/// <summary>
/// Determines whether the given value object matches this type
/// </summary>
/// <returns><c>true</c> if the value matches this type; otherwise, <c>false</c>.</returns>
/// <param name="value">the value to test.</param>
public bool IsValueOfType(MmsValue value)
{
return MmsVariableSpecification_isValueOfType(self, value.valueReference);
}
IEnumerator IEnumerable.GetEnumerator ()
{
return new MmsVariableSpecificationEnumerator (this);

@ -130,7 +130,7 @@ namespace IEC61850
static extern void ClientReportControlBlock_setPurgeBuf (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool purgeBuf);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern Int32 ClientReportControlBlock_getResvTms (IntPtr self);
static extern Int16 ClientReportControlBlock_getResvTms (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void ClientReportControlBlock_setResvTms (IntPtr self, Int16 resvTms);
@ -711,6 +711,51 @@ namespace IEC61850
flagOptFlds = true;
}
/// <summary>
/// Gets the ResvTms (reservation time) value
/// </summary>
/// <remarks>
/// Only for BRCB.
/// Value of -1 indicate the BRCB is exclusively reserved for a set of client based upon configuration.
/// Value of 0 means that the BRCB is not reserved.
/// Positive value indicates that the BRCB is reserved dynamically and the value is the number of
/// seconds for reservation after association loss.
/// </remarks>
/// <returns>The reservation time</returns>
public Int16 GetResvTms()
{
return ClientReportControlBlock_getResvTms (self);
}
/// <summary>
/// Sets the ResvTms (reservation time) value
/// </summary>
/// <param name="resvTms">the reservation time value</param>
public void SetResvTms(Int16 resvTms)
{
ClientReportControlBlock_setResvTms (self, resvTms);
flagResvTms = true;
}
/// <summary>
/// Gets the current owner of the RCB
/// </summary>
/// <returns>The owner information, or null when no owner information is available.</returns>
public byte[] GetOwner()
{
IntPtr mmsValuePtr = ClientReportControlBlock_getOwner(self);
if (mmsValuePtr != IntPtr.Zero)
{
MmsValue octetStringVal = new MmsValue(mmsValuePtr);
return octetStringVal.getOctetString();
}
else
return null;
}
}
}

@ -1,7 +1,7 @@
/*
* Reporting.cs
*
* Copyright 2014 Michael Zillgith
* Copyright 2014-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -147,6 +147,15 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientReport_getDataReference(IntPtr self, int elementIndex);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_hasSubSeqNum(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt16 ClientReport_getSubSeqNum(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern bool ClientReport_getMoreSeqmentsFollow(IntPtr self);
private IntPtr self;
private IntPtr dataSetValues = IntPtr.Zero;
@ -213,16 +222,55 @@ namespace IEC61850
return ClientReport_getBufOvfl(self);
}
/// <summary>
/// Indicates if the report contains a sequence number (SeqNum) field
/// </summary>
/// <returns><c>true</c> if this instance has SeqNum; otherwise, <c>false</c>.</returns>
public bool HasSeqNum ()
{
return ClientReport_hasSeqNum(self);
}
/// <summary>
/// Gets the value of the SeqNum field
/// </summary>
/// <returns>The report sequence number</returns>
public UInt16 GetSeqNum ()
{
return ClientReport_getSeqNum(self);
}
/// <summary>
/// Indicates if the report contains a sub sequence number (SubSeqNum) and more segments follow (MoreSegmentsFollow) field
/// </summary>
/// <returns><c>true</c> if this instance has SubSeqNum and MoreSegmentsFollow; otherwise, <c>false</c>.</returns>
public bool HasSubSeqNum()
{
return ClientReport_hasSubSeqNum(self);
}
/// <summary>
/// Gets the sub sequence number (SubSeqNum) value of a segmented report
/// </summary>
/// <returns>The sub sequence number.</returns>
public UInt16 GetSubSeqNum()
{
return ClientReport_getSubSeqNum(self);
}
/// <summary>
/// Gets the more segments follow (MoreSegmentsFollow) flag
/// </summary>
/// <returns><c>true</c>, if more report segments follow, <c>false</c> otherwise.</returns>
public bool GetMoreSegmentsFollow()
{
return ClientReport_getMoreSeqmentsFollow(self);
}
/// <summary>
/// Determines whether this report contains reason for inclusion information
/// </summary>
/// <returns><c>true</c> if this report contains reason for inclusion information; otherwise, <c>false</c>.</returns>
public bool HasReasonForInclusion ()
{
return ClientReport_hasReasonForInclusion(self);

@ -0,0 +1,78 @@
using System;
using IEC61850.Client;
using IEC61850.Common;
namespace client_examples_setting_groups
{
/// <summary>
/// This class is intended to show how to use setting groups from the client side.
/// It works with server_example_setting_groups.
/// </summary>
class SettingGroupsClientExample
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
IedConnection con = new IedConnection ();
string hostname;
if (args.Length > 0)
hostname = args[0];
else
hostname = "127.0.0.1";
int port = 102;
if (args.Length > 1)
port = Int32.Parse(args [1]);
Console.WriteLine("Connect to " + hostname);
try
{
con.Connect(hostname, port);
/* Get variable specification of the SGCB (optional) */
MmsVariableSpecification sgcbVarSpec = con.GetVariableSpecification("DEMOPROT/LLN0.SGCB", FunctionalConstraint.SP);
/* Read SGCB */
MmsValue sgcbVal = con.ReadValue("DEMOPROT/LLN0.SGCB", FunctionalConstraint.SP);
Console.WriteLine("NumOfSG: {0}", sgcbVal.GetChildValue("NumOfSG", sgcbVarSpec).ToString());
Console.WriteLine("ActSG: {0}", sgcbVal.GetChildValue("ActSG", sgcbVarSpec).ToString());
Console.WriteLine("EditSG: {0}", sgcbVal.GetChildValue("EditSG", sgcbVarSpec).ToString());
Console.WriteLine("CnfEdit: {0}", sgcbVal.GetChildValue("CnfEdit", sgcbVarSpec).ToString());
/* Set active setting group */
con.WriteValue("DEMOPROT/LLN0.SGCB.ActSG", FunctionalConstraint.SP, new MmsValue((uint) 2));
/* Set edit setting group */
con.WriteValue("DEMOPROT/LLN0.SGCB.EditSG", FunctionalConstraint.SP, new MmsValue((uint) 1));
/* Change a setting group value */
con.WriteValue("DEMOPROT/PTOC1.StrVal.setMag.f", FunctionalConstraint.SE, new MmsValue(1.0f));
/* Confirm new setting group values */
con.WriteValue("DEMOPROT/LLN0.SGCB.CnfEdit", FunctionalConstraint.SP, new MmsValue(true));
/* Read SGCB */
con.ReadValue("DEMOPROT/LLN0.SGCB", FunctionalConstraint.SP);
Console.WriteLine("ActSG: {0}",sgcbVal.GetChildValue("ActSG", sgcbVarSpec).ToString());
con.Abort();
}
catch (IedConnectionException e)
{
Console.WriteLine(e.Message);
}
// release all resources - do NOT use the object after this call!!
con.Dispose ();
}
}
}

@ -0,0 +1,27 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("client-example-setting-group")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("mzillgit")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0DA95476-B149-450B-AC36-01CEECFC1A43}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>clientexamplesettinggroup</RootNamespace>
<AssemblyName>client-example-setting-group</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Externalconsole>true</Externalconsole>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\IEC61850forCSharp\IEC61850.NET.csproj">
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
<Name>IEC61850.NET</Name>
</ProjectReference>
</ItemGroup>
</Project>

@ -44,6 +44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sv_subscriber", "sv_subscri
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_server_example", "tls_server_example\tls_server_example.csproj", "{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_setting_groups", "client_example_setting_groups\client_example_setting_groups.csproj", "{0DA95476-B149-450B-AC36-01CEECFC1A43}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -54,6 +56,10 @@ Global
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.Build.0 = Release|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DA95476-B149-450B-AC36-01CEECFC1A43}.Release|Any CPU.Build.0 = Release|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.ActiveCfg = Release|Any CPU

@ -69,6 +69,17 @@ namespace example1
Console.WriteLine("Read data set " + dataSet.GetReference());
/* read multiple variables (WARNING: this is not IEC 61850 standard compliant but might
* be supported by most servers).
*/
MmsConnection mmsConnection = con.GetMmsConnection();
MmsValue result = mmsConnection.ReadMultipleVariables("simpleIOGenericIO", new List<string>() {
"GGIO1$ST$Ind1", "GGIO1$ST$Ind1", "GGIO1$ST$Ind1","GGIO1$ST$Ind1"
});
Console.WriteLine(result.ToString());
con.Abort();
}
catch (IedConnectionException e)

@ -104,6 +104,10 @@ namespace reporting
rcb2.SetRCBValues();
rcb2.GetRCBValues();
Console.WriteLine("RCB: " + rcbReference2 + " Owner: " + BitConverter.ToString(rcb2.GetOwner()));
rcb3.GetRCBValues();
if (rcb3.IsBuffered())

@ -276,7 +276,10 @@ namespace tests
[Test ()]
public void AccessDataModelClientServer()
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("../../model.cfg");
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("../../model.cfg");
ModelNode ind1 = iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.Ind1.stVal");

@ -27,6 +27,7 @@
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
/* Include the generated header with the model access handles */
@ -92,6 +93,7 @@ setupSVPublisher(const char* svInterface)
vol4q = SVPublisher_ASDU_addQuality(asdu);
SVPublisher_ASDU_setSmpCntWrap(asdu, 4000);
SVPublisher_ASDU_setRefrTm(asdu, 0);
SVPublisher_setupComplete(svPublisher);
}
@ -220,6 +222,8 @@ main(int argc, char** argv)
SVPublisher_ASDU_setINT32(asdu, vol4, voltageN);
SVPublisher_ASDU_setQuality(asdu, vol4q, q);
SVPublisher_ASDU_setRefrTm(asdu, Hal_getTimeInMs());
SVPublisher_ASDU_setSmpCnt(asdu, (uint16_t) sampleCount);
SVPublisher_publish(svPublisher);

@ -67,18 +67,28 @@ int main(int argc, char** argv) {
LinkedList_destroyStatic(newDataSetEntries);
printf("error: %i\n", error);
/* read data set */
ClientDataSet clientDataSet;
if (error == IED_ERROR_OK ) {
clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.AnalogueValues", NULL);
/* read new data set */
ClientDataSet clientDataSet;
printDataSetValues(ClientDataSet_getValues(clientDataSet));
clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.AnalogueValues", NULL);
Thread_sleep(1000);
if (error == IED_ERROR_OK) {
printDataSetValues(ClientDataSet_getValues(clientDataSet));
IedConnection_deleteDataSet(con, &error, "simpleIOGenericIO/LLN0.AnalogueValues");
ClientDataSet_destroy(clientDataSet);
}
else {
printf("Failed to read data set (error code: %d)\n", error);
}
IedConnection_deleteDataSet(con, &error, "simpleIOGenericIO/LLN0.AnalogueValues");
}
else {
printf("Failed to create data set (error code: %d)\n", error);
}
IedConnection_close(con);
}

@ -100,9 +100,10 @@ main(int argc, char** argv)
IedServerConfig_enableFileService(config, false);
/* disable dynamic data set service */
IedServerConfig_enableDynamicDataSetService(config, false);
IedServerConfig_enableDynamicDataSetService(config, true);
IedServerConfig_enableLogService(config, true);
/* disable log service */
IedServerConfig_enableLogService(config, false);
/* set maximum number of clients */
IedServerConfig_setMaxMmsConnections(config, 2);

@ -26,6 +26,7 @@ if(WIN32)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")
message("Found winpcap -> compile ethernet HAL layer (required for GOOSE/SV support)")
set(WITH_WPCAP 1)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Include")
else()
message("winpcap not found -> skip ethernet HAL layer (no GOOSE/SV support)")
endif()
@ -154,7 +155,7 @@ ENDIF(MINGW)
iF(WITH_WPCAP)
target_link_libraries(hal
${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/lib/wpcap.lib
${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/lib/packet.lib
${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib
${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/packet.lib
)
ENDIF(WITH_WPCAP)

@ -155,7 +155,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
memset(&buffer, 0x00, sizeof(buffer));
strcpy(buffer.ifr_name, interfaceId);
strncpy(buffer.ifr_name, interfaceId, IFNAMSIZ);
ioctl(sock, SIOCGIFHWADDR, &buffer);

@ -313,6 +313,8 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr)
char* interfaceName = getInterfaceName((int) interfaceIndex);
getAdapterMacAddress(interfaceName, addr);
free(interfaceName);
}
@ -329,9 +331,12 @@ Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress)
if ((pcapSocket = pcap_open_live(interfaceName, 65536, PCAP_OPENFLAG_PROMISCUOUS, 10, errbuf)) == NULL)
{
printf("Open ethernet socket failed for device %s\n", interfaceName);
free(interfaceName);
return NULL;
}
free(interfaceName);
EthernetSocket ethernetSocket = (EthernetSocket) calloc(1, sizeof(struct sEthernetSocket));
ethernetSocket->rawSocket = pcapSocket;

@ -1,8 +1,12 @@
# The SWIG functions/macros used in this module, swig_add_module and swig_add_library
# are not available in CMake versions earlier than 3.8
# cmake_minimum_required(VERSION 3.8)
find_package(SWIG REQUIRED)
include(${SWIG_USE_FILE})
find_package(PythonLibs REQUIRED)
find_package(PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED)
find_package(PythonInterp ${BUILD_PYTHON_VERSION} REQUIRED)
find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT REQUIRED)
include_directories(${PYTHON_INCLUDE_PATH})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
@ -16,7 +20,7 @@ else()
set(LIBS iec61850-shared)
endif()
if(${SWIG_VERSION} VERSION_LESS 3.0)
if(${CMAKE_VERSION} VERSION_LESS 3.8)
swig_add_module(iec61850 python iec61850.i)
else()
swig_add_library(iec61850
@ -30,7 +34,7 @@ swig_link_libraries(iec61850 ${PYTHON_LIBRARIES} ${LIBS})
# Finding python modules install path
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c
"import site, sys; sys.stdout.write(site.getsitepackages()[-1])"
"from distutils.sysconfig import get_python_lib; import.sys; sys.stdout.write(get_python_lib())"
OUTPUT_VARIABLE PYTHON_SITE_DIR
)

@ -17,7 +17,7 @@
#include "platform_endian.h"
#define LIBIEC61850_VERSION "1.3.0"
#define LIBIEC61850_VERSION "1.3.1"
#ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"

@ -1,7 +1,7 @@
/*
* goose_publisher.c
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -248,6 +248,10 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
goosePduLength += BerEncoder_determineEncodedStringSize(self->goCBRef);
uint32_t timeAllowedToLive = self->timeAllowedToLive;
goosePduLength += 2 + BerEncoder_UInt32determineEncodedSize(timeAllowedToLive);
goosePduLength += BerEncoder_determineEncodedStringSize(self->dataSetRef);
if (self->goID != NULL)
@ -255,10 +259,6 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
else
goosePduLength += BerEncoder_determineEncodedStringSize(self->goCBRef);
uint32_t timeAllowedToLive = self->timeAllowedToLive;
goosePduLength += 2 + BerEncoder_UInt32determineEncodedSize(timeAllowedToLive);
goosePduLength += 2 + 8; /* for T (UTCTIME) */
goosePduLength += 2 + BerEncoder_UInt32determineEncodedSize(self->sqNum);
@ -289,7 +289,9 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe
goosePduLength += allDataSize;
if (goosePduLength > maxPayloadSize)
uint32_t payloadSize = 1 + BerEncoder_determineLengthSize(goosePduLength) + goosePduLength;
if (payloadSize > maxPayloadSize)
return -1;
/* Step 2 - encode to buffer */

@ -162,8 +162,13 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlVal");
if (MmsVariableSpecification_getType(ctlVal) == MMS_STRUCTURE)
isAPC = true;
if (ctlVal == NULL)
ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "setMag");
if (ctlVal) {
if (MmsVariableSpecification_getType(ctlVal) == MMS_STRUCTURE)
isAPC = true;
}
MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm");
@ -211,20 +216,24 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
self->analogValue = NULL;
/* Check for T element type (Binary time -> Ed.1,UTC time -> Ed.2) */
if (MmsVariableSpecification_getType(t) == MMS_BINARY_TIME)
self->edition = 1;
if (t) {
if (MmsVariableSpecification_getType(t) == MMS_BINARY_TIME)
self->edition = 1;
else
self->edition = 2;
}
else
self->edition = 2;
self->edition = 1;
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: Detected edition %i control\n", self->edition);
private_IedConnection_addControlClient(connection, self);
free_varspec:
free_varspec:
MmsVariableSpecification_destroy(ctlVarSpec);
exit_function:
exit_function:
return self;
}
@ -596,24 +605,16 @@ ControlObjectClient_select(ControlObjectClient self)
goto exit_function;
}
char sboReference[130];
snprintf(sboReference, 129, "%s/%s", domainId, itemId);
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
if (strcmp(MmsValue_toString(value), "") == 0) {
if (DEBUG_IED_CLIENT)
printf("select-response-\n");
}
else if (strcmp(MmsValue_toString(value), sboReference) == 0) {
else {
if (DEBUG_IED_CLIENT)
printf("select-response+: (%s)\n", MmsValue_toString(value));
selected = true;
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-response: (%s)\n", MmsValue_toString(value));
}
}
else {
if (DEBUG_IED_CLIENT)

@ -3,7 +3,7 @@
*
* Client implementation for IEC 61850 reporting.
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -56,11 +56,15 @@ struct sClientReport
bool hasConfRev;
bool hasTimestamp;
bool hasBufOverflow;
bool hasSubSequenceNumber;
uint64_t timestamp;
uint16_t seqNum;
uint32_t confRev;
bool bufOverflow;
uint16_t subSeqNum;
bool moreSegementsFollow;
};
char*
@ -242,6 +246,24 @@ ClientReport_getDataSetValues(ClientReport self)
return self->dataSetValues;
}
bool
ClientReport_hasSubSeqNum(ClientReport self)
{
return self->hasSubSequenceNumber;
}
uint16_t
ClientReport_getSubSeqNum(ClientReport self)
{
return self->subSeqNum;
}
bool
ClientReport_getMoreSeqmentsFollow(ClientReport self)
{
return self->moreSegementsFollow;
}
static ClientReport
lookupReportHandler(IedConnection self, const char* rcbReference)
{
@ -386,6 +408,7 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
matchingReport->hasConfRev = false;
matchingReport->hasDataSetName = false;
matchingReport->hasBufOverflow = false;
matchingReport->hasSubSequenceNumber = false;
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received report with ID %s\n", MmsValue_toString(rptIdValue));
@ -535,10 +558,43 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
inclusionIndex++;
}
/* handle segmentation fields (check ReportedOptFlds.segmentation) */
if (MmsValue_getBitStringBit(optFlds, 9) == true) {
MmsValue* subSeqNum = MmsValue_getElement(value, inclusionIndex);
inclusionIndex++;
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (SubSeqNum)\n");
goto exit_function;
}
else {
matchingReport->subSeqNum = (uint16_t) MmsValue_toUint32(subSeqNum);
}
MmsValue* moreSegmentsFollow = MmsValue_getElement(value, inclusionIndex);
inclusionIndex++;
if ((moreSegmentsFollow == NULL) || (MmsValue_getType(moreSegmentsFollow) != MMS_BOOLEAN)) {
if ((subSeqNum == NULL) || (MmsValue_getType(subSeqNum) != MMS_UNSIGNED)) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: received malformed report (MoreSegmentsFollow)\n");
goto exit_function;
}
}
else {
matchingReport->moreSegementsFollow = MmsValue_getBoolean(moreSegmentsFollow);
}
/* skip segmentation fields */
if (MmsValue_getBitStringBit(optFlds, 9) == true)
inclusionIndex += 2;
matchingReport->hasSequenceNumber = true;
}
else {
matchingReport->subSeqNum = 0;
matchingReport->moreSegementsFollow = false;
}
MmsValue* inclusion = MmsValue_getElement(value, inclusionIndex);

@ -1057,6 +1057,42 @@ ClientReport_getDataReference(ClientReport self, int elementIndex);
uint64_t
ClientReport_getTimestamp(ClientReport self);
/**
* \brief indicates if the report contains a sub sequence number and a more segments follow flags (for segmented reporting)
*
* \param self the ClientReport instance
*
* \returns true if the report contains sub-sequence-number and more-follows-flag, false otherwise
*/
bool
ClientReport_hasSubSeqNum(ClientReport self);
/**
* \brief get the sub sequence number of the report (for segmented reporting)
*
* Returns the sub sequence number of the report. This is 0 for the first report of a segmented report and
* will be increased by one for each report segment.
*
* \param self the ClientReport instance
*
* \return the sub sequence number of the last received report message.
*/
uint16_t
ClientReport_getSubSeqNum(ClientReport self);
/**
* \brief get the more segments follow flag of the received report segment (for segmented reporting)
*
* Will return true in case this is part of a segmented report and more report segments will follow or false, if
* the current report is not a segmented report or is the last segment of a segmented report.
*
* \param self the ClientReport instance
*
* \return true when more segments of the current report will follow, false otherwise
*/
bool
ClientReport_getMoreSeqmentsFollow(ClientReport self);
/**
* \brief get the reason for inclusion of as a human readable string
*

@ -475,8 +475,12 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
for (i = 0; i < self->dataSet->elementCount; i++)
self->inclusionFlags[i] = REPORT_CONTROL_NONE;
ReportControl_unlockNotify(self);
MmsServerConnection_sendInformationReportVMDSpecific(self->clientConnection, "RPT", reportElements, false);
ReportControl_lockNotify(self);
/* Increase sequence number */
self->sqNum++;
@ -2393,8 +2397,12 @@ sendNextReportEntry(ReportControl* self)
}
}
ReportControl_unlockNotify(self);
MmsServerConnection_sendInformationReportVMDSpecific(self->clientConnection, "RPT", (LinkedList) reportElements, false);
ReportControl_lockNotify(self);
/* Increase sequence number */
self->sqNum++;
MmsValue_setUint32(sqNum, self->sqNum);
@ -2455,12 +2463,13 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
/* send current events in event buffer before GI report */
if (rc->triggered) {
rc->triggered = false;
if (rc->buffered)
enqueueReport(rc, false, false, currentTimeInMs);
else
sendReport(rc, false, false);
rc->triggered = false;
}
if (rc->buffered)

@ -63,6 +63,8 @@ struct sSVPublisher_ASDU {
uint16_t smpRate;
uint8_t* smpCntBuf;
uint8_t* refrTmBuf;
uint8_t* smpSynchBuf;
SVPublisher_ASDU _next;
};
@ -386,11 +388,13 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP
/* RefrTm */
if (self->hasRefrTm) {
bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos);
self->refrTmBuf = buffer + bufPos;
bufPos = encodeUtcTime(self->refrTm, buffer, bufPos);
}
/* SmpSynch */
bufPos = BerEncoder_encodeTL(0x85, 1, buffer, bufPos);
self->smpSynchBuf = buffer + bufPos;
buffer[bufPos++] = self->smpSynch;
/* SmpRate */
@ -651,7 +655,8 @@ SVPublisher_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value)
{
self->smpCnt = value;
encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0);
if (self->smpCntBuf != NULL)
encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0);
}
void
@ -665,7 +670,14 @@ SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self)
{
self->smpCnt = ((self->smpCnt + 1) % self->smpCntLimit);
encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0);
if (self->smpCntBuf != NULL)
encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0);
}
void
SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self)
{
self->hasRefrTm = true;
}
void
@ -673,6 +685,9 @@ SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm)
{
self->hasRefrTm = true;
self->refrTm = refrTm;
if (self->refrTmBuf != NULL)
encodeUtcTime(self->refrTm, self->refrTmBuf, 0);
}
void
@ -689,6 +704,12 @@ SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate)
self->smpRate = smpRate;
}
void
SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch)
{
self->smpSynch = smpSynch;
*(self->smpSynchBuf) = self->smpSynch;
}
/*******************************************************************
* Wrapper functions to support old API (remove in future versions)

@ -1,7 +1,7 @@
/*
* sv_publisher.h
*
* Copyright 2016 Michael Zillgith
* Copyright 2016-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -304,6 +304,16 @@ SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self);
void
SVPublisher_ASDU_setSmpCntWrap(SVPublisher_ASDU self, uint16_t value);
/**
* \brief Enables the transmission of refresh time attribute of the ASDU
*
* The refresh time is the time when the data in put into the sampled values buffer
*
* \param[in] self the Sampled Values ASDU instance.
*/
void
SVPublisher_ASDU_enableRefrTm(SVPublisher_ASDU self);
/**
* \brief Set the refresh time attribute of the ASDU.
*
@ -318,8 +328,10 @@ SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm);
* The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per second or seconds per sample.
* If it is missing, the default value is samples per period.
*
* NOTE: Function has to be called before calling \ref SVPublisher_setupComplete
*
* \param[in] self the Sampled Values ASDU instance.
* \param smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE
* \param[in] smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE
*/
void
SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod);
@ -330,12 +342,31 @@ SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod);
* The attribute SmpRate shall specify the sample rate.
* The value shall be interpreted depending on the value of the SmpMod attribute.
*
* NOTE: Function has to be called before calling \ref SVPublisher_setupComplete
*
* \param[in] self the Sampled Values ASDU instance.
* \param smpRate Amount of samples (default per nominal period, see SmpMod).
* \param[in] smpRate Amount of samples (default per nominal period, see SmpMod).
*/
void
SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate);
/**
* \brief Set the clock synchronization information
*
* Default value is not synchronized (0).
* Possible values are:
* 0 = SV are not synchronized by an external clock signal.
* 1 = SV are synchronized by a clock signal from an unspecified local area clock.
* 2 = SV are synchronized by a global area clock signal (time traceable).
* 5 to 254 = SV are synchronized by a clock signal from a local area clock identified by this value.
* 3;4;255 = Reserved values Do not use.
*
* \param[in] self the Sampled Values ASDU instance.
* \param[in] smpSynch the clock synchronization state
*/
void
SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch);
/**@} @}*/
#ifndef DEPRECATED

@ -621,3 +621,6 @@ EXPORTS
IedServerConfig_setMaxDomainSpecificDataSets
IedServerConfig_getMaxDomainSpecificDataSets
IedServer_setReadAccessHandler
ClientReport_hasSubSeqNum
ClientReport_getSubSeqNum
ClientReport_getMoreSeqmentsFollow

@ -749,3 +749,8 @@ EXPORTS
IedServerConfig_setMaxDomainSpecificDataSets
IedServerConfig_getMaxDomainSpecificDataSets
IedServer_setReadAccessHandler
ClientReport_hasSubSeqNum
ClientReport_getSubSeqNum
ClientReport_getMoreSeqmentsFollow
SVPublisher_ASDU_enableRefrTm
SVPublisher_ASDU_setSmpSynch

@ -1,5 +1,7 @@
package com.libiec61850.scl.model;
import java.text.SimpleDateFormat;
/*
* DataModelValue.java
*
@ -24,6 +26,7 @@ package com.libiec61850.scl.model;
*/
import java.util.Base64;
import java.util.Date;
import com.libiec61850.scl.types.EnumerationType;
import com.libiec61850.scl.types.IllegalValueException;
@ -86,7 +89,7 @@ public class DataModelValue {
if (trimmedValue.isEmpty())
this.value = new Long(0);
else
this.value = Long.decode(trimmedValue);
this.value = Long.parseLong(trimmedValue);
break;
case BOOLEAN:
@ -162,6 +165,25 @@ public class DataModelValue {
this.value = null;
System.out.println("Warning: Initialization of QUALITY is unsupported!");
break;
case TIMESTAMP:
case ENTRY_TIME:
try {
String modValueString = value.replace(',', '.');
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS");
Date date = parser.parse(modValueString);
this.value = new Long(date.toInstant().toEpochMilli());
}
catch (java.text.ParseException e) {
this.value = null;
System.out.println("Warning: Val element does not contain a valid time stamp: " + e.getMessage());
}
break;
default:
throw new IllegalValueException("Unsupported type " + type.toString() + " value: " + value);
}

@ -805,6 +805,11 @@ public class StaticModelGenerator {
case FLOAT64:
buffer.append("MmsValue_newDouble(" + value.getValue() + ");");
break;
case TIMESTAMP:
buffer.append("MmsValue_newUtcTimeByMsTime("+ value.getValue() + ");");
break;
default:
System.out.println("Unknown default value for " + daName + " type: " + dataAttribute.getType());
buffer.append("NULL;");

Loading…
Cancel
Save