- update to current development version
parent
48d717f12c
commit
a0f67f9a5d
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* MmsVariableSpecification.cs
|
||||
*
|
||||
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
using System;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace IEC61850
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
/// <summary>
|
||||
/// MMS variable specification. This class is used to represent an MMS variable type definition.
|
||||
/// </summary>
|
||||
public class MmsVariableSpecification : IEnumerable
|
||||
{
|
||||
[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);
|
||||
|
||||
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern int MmsVariableSpecification_getType(IntPtr self);
|
||||
|
||||
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern IntPtr MmsVariableSpecification_getName(IntPtr self);
|
||||
|
||||
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern int MmsVariableSpecification_getSize(IntPtr self);
|
||||
|
||||
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern IntPtr MmsVariableSpecification_getChildSpecificationByIndex(IntPtr self, int index);
|
||||
|
||||
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern IntPtr MmsVariableSpecification_getArrayElementSpecification(IntPtr self);
|
||||
|
||||
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
||||
static extern int MmsVariableSpecification_getExponentWidth(IntPtr self);
|
||||
|
||||
private IntPtr self;
|
||||
private bool responsableForDeletion;
|
||||
|
||||
internal MmsVariableSpecification (IntPtr self)
|
||||
{
|
||||
this.self = self;
|
||||
this.responsableForDeletion = false;
|
||||
}
|
||||
|
||||
internal MmsVariableSpecification (IntPtr self, bool responsableForDeletion)
|
||||
{
|
||||
this.self = self;
|
||||
this.responsableForDeletion = responsableForDeletion;
|
||||
}
|
||||
|
||||
~MmsVariableSpecification ()
|
||||
{
|
||||
if (responsableForDeletion)
|
||||
MmsVariableSpecification_destroy(self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MmsValue type of the variable
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The MmsType of the variable
|
||||
/// </returns>
|
||||
public new MmsType GetType ()
|
||||
{
|
||||
return (MmsType) MmsVariableSpecification_getType(self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the array elements.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The array element type.
|
||||
/// </returns>
|
||||
/// <exception cref="MmsValueException">This exception is thrown if the value is not of type MMS_ARRAY</exception>
|
||||
public MmsVariableSpecification getArrayElementType ()
|
||||
{
|
||||
if (GetType() == MmsType.MMS_ARRAY) {
|
||||
IntPtr varSpecPtr = MmsVariableSpecification.MmsVariableSpecification_getArrayElementSpecification(self);
|
||||
return new MmsVariableSpecification(varSpecPtr);
|
||||
}
|
||||
else
|
||||
throw new MmsValueException ("specification is of wrong type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the element specification of a structure element
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The element of the structure at given index
|
||||
/// </returns>
|
||||
/// <param name='index'>
|
||||
/// Index.
|
||||
/// </param>
|
||||
public MmsVariableSpecification GetElement (int index)
|
||||
{
|
||||
if (GetType () == MmsType.MMS_STRUCTURE) {
|
||||
|
||||
if ((index >= 0) && (index < Size ())) {
|
||||
IntPtr varSpecPtr = MmsVariableSpecification_getChildSpecificationByIndex(self, index);
|
||||
return new MmsVariableSpecification(varSpecPtr);
|
||||
}
|
||||
else
|
||||
throw new MmsValueException ("Index out of bounds");
|
||||
}
|
||||
else
|
||||
throw new MmsValueException ("specification is of wrong type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the variable
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The name.
|
||||
/// </returns>
|
||||
public string GetName ()
|
||||
{
|
||||
IntPtr namePtr = MmsVariableSpecification_getName(self);
|
||||
|
||||
return Marshal.PtrToStringAnsi (namePtr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the "size" of the variable (array size, number of structure elements ...)
|
||||
/// </summary>
|
||||
public int Size ()
|
||||
{
|
||||
return MmsVariableSpecification_getSize(self);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return new MmsVariableSpecificationEnumerator (this);
|
||||
}
|
||||
|
||||
private class MmsVariableSpecificationEnumerator : IEnumerator
|
||||
{
|
||||
private MmsVariableSpecification value;
|
||||
private int index = -1;
|
||||
|
||||
public MmsVariableSpecificationEnumerator (MmsVariableSpecification value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
#region IEnumerator Members
|
||||
public void Reset ()
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
|
||||
public object Current {
|
||||
|
||||
get { return value.GetElement (index);}
|
||||
}
|
||||
|
||||
public bool MoveNext ()
|
||||
{
|
||||
index++;
|
||||
|
||||
if (index >= value.Size ())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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("example3")]
|
||||
[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,48 @@
|
||||
using System;
|
||||
using IEC61850.Client;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace example3
|
||||
{
|
||||
class MainClass
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
IedConnection con = new IedConnection ();
|
||||
|
||||
string hostname;
|
||||
|
||||
if (args.Length > 0)
|
||||
hostname = args[0];
|
||||
else
|
||||
hostname = "localhost";
|
||||
|
||||
Console.WriteLine("Connect to " + hostname);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
IsoConnectionParameters parameters = con.GetConnectionParameters();
|
||||
|
||||
parameters.SetRemoteAddresses(1,1, new byte[] {0x00, 0x01, 0x02, 0x03});
|
||||
|
||||
con.ConnectTimeout = 10000;
|
||||
|
||||
con.Connect(hostname, 102);
|
||||
|
||||
List<string> serverDirectory = con.GetServerDirectory(false);
|
||||
|
||||
foreach (string entry in serverDirectory)
|
||||
{
|
||||
Console.WriteLine("LD: " + entry);
|
||||
}
|
||||
|
||||
con.Release();
|
||||
}
|
||||
catch (IedConnectionException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?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>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>example3</RootNamespace>
|
||||
<AssemblyName>example3</AssemblyName>
|
||||
</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>none</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="Main.cs" />
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
|
||||
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
|
||||
<Name>IEC61850forCSharp</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -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("files")]
|
||||
[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,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using IEC61850.Client;
|
||||
using IEC61850.Common;
|
||||
using System.IO;
|
||||
|
||||
namespace files
|
||||
{
|
||||
class MainClass
|
||||
{
|
||||
public static void printFiles (IedConnection con, string prefix, string parent)
|
||||
{
|
||||
List<FileDirectoryEntry> files = con.GetFileDirectory (parent);
|
||||
|
||||
foreach (FileDirectoryEntry file in files) {
|
||||
Console.WriteLine(prefix + file.GetFileName() + "\t" + file.GetFileSize() + "\t" +
|
||||
MmsValue.MsTimeToDateTimeOffset(file.GetLastModified()));
|
||||
|
||||
if (file.GetFileName().EndsWith("/")) {
|
||||
printFiles (con, prefix + " ", parent + file.GetFileName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool getFileHandler (object parameter, byte[] data)
|
||||
{
|
||||
Console.WriteLine("received " + data.Length + " bytes");
|
||||
|
||||
BinaryWriter binWriter = (BinaryWriter) parameter;
|
||||
|
||||
binWriter.Write(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
IedConnection con = new IedConnection ();
|
||||
|
||||
string hostname;
|
||||
|
||||
if (args.Length > 0)
|
||||
hostname = args[0];
|
||||
else
|
||||
hostname = "10.0.2.2";
|
||||
|
||||
Console.WriteLine("Connect to " + hostname);
|
||||
|
||||
try
|
||||
{
|
||||
con.Connect(hostname, 102);
|
||||
|
||||
Console.WriteLine ("Files in server root directory:");
|
||||
List<string> serverDirectory = con.GetServerDirectory(true);
|
||||
|
||||
foreach (string entry in serverDirectory) {
|
||||
Console.WriteLine(entry);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine ("File directory tree at server:");
|
||||
printFiles(con, "", "");
|
||||
Console.WriteLine();
|
||||
|
||||
string filename = "IEDSERVER.BIN";
|
||||
|
||||
Console.WriteLine("Download file " + filename);
|
||||
|
||||
/* Download file from server and write it to a new local file */
|
||||
FileStream fs = new FileStream(filename, FileMode.Create);
|
||||
BinaryWriter w = new BinaryWriter(fs);
|
||||
|
||||
con.GetFile(filename, new IedConnection.GetFileHandler(getFileHandler), w);
|
||||
|
||||
fs.Close();
|
||||
|
||||
con.Abort();
|
||||
}
|
||||
catch (IedConnectionException e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?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>
|
||||
<ProductVersion>10.0.0</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{77127456-19B9-4D1A-AEF9-40F8D1C5695E}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>files</RootNamespace>
|
||||
<AssemblyName>files</AssemblyName>
|
||||
</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>none</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="AssemblyInfo.cs" />
|
||||
<Compile Include="FileServicesExample.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IEC61850forCSharp\IEC61850forCSharp.csproj">
|
||||
<Project>{C35D624E-5506-4560-8074-1728F1FA1A4D}</Project>
|
||||
<Name>IEC61850forCSharp</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,24 +1,9 @@
|
||||
INCLUDES = -I$(LIBIEC_HOME)/config
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/common
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_presentation
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_session
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_cotp
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_acse
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_mms/common
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_mms/client
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_mms/server
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_client
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_common
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/iso_server
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/common/inc
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/inc
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/inc_private
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/mms/asn1
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iedcommon
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iedserver/mms_mapping
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iedserver/model
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iedserver
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iedclient
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/hal
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/hal/thread
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/hal/socket
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/hal/filesystem
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/hal/time
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iec61850/inc
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/iec61850/inc_private
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/hal/inc
|
||||
INCLUDES += -I$(LIBIEC_HOME)/src/goose
|
||||
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* lib_memory.c
|
||||
*
|
||||
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "lib_memory.h"
|
||||
|
||||
static MemoryExceptionHandler exceptionHandler = NULL;
|
||||
static void* exceptionHandlerParameter = NULL;
|
||||
|
||||
static void
|
||||
noMemoryAvailableHandler(void)
|
||||
{
|
||||
if (exceptionHandler != NULL)
|
||||
exceptionHandler(exceptionHandlerParameter);
|
||||
}
|
||||
|
||||
void
|
||||
Memory_installExceptionHandler(MemoryExceptionHandler handler, void* parameter)
|
||||
{
|
||||
exceptionHandler = handler;
|
||||
exceptionHandlerParameter = parameter;
|
||||
}
|
||||
|
||||
void*
|
||||
Memory_malloc(size_t size)
|
||||
{
|
||||
void* memory = malloc(size);
|
||||
|
||||
if (memory == NULL)
|
||||
noMemoryAvailableHandler();
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
Memory_calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void* memory = calloc(nmemb, size);
|
||||
|
||||
if (memory == NULL)
|
||||
noMemoryAvailableHandler();
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
Memory_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void* memory = realloc(ptr, size);
|
||||
|
||||
if (memory == NULL)
|
||||
noMemoryAvailableHandler();
|
||||
|
||||
return memory;
|
||||
}
|
||||
|
||||
void
|
||||
Memory_free(void* memb)
|
||||
{
|
||||
free(memb);
|
||||
}
|
||||
|
@ -0,0 +1,778 @@
|
||||
/*
|
||||
* goose_receiver.c
|
||||
*
|
||||
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
|
||||
#include "stack_config.h"
|
||||
#include "goose_subscriber.h"
|
||||
#include "hal_ethernet.h"
|
||||
#include "hal_thread.h"
|
||||
|
||||
#include "ber_decode.h"
|
||||
|
||||
#include "mms_value.h"
|
||||
#include "mms_value_internal.h"
|
||||
#include "linked_list.h"
|
||||
|
||||
#include "goose_receiver.h"
|
||||
#include "goose_receiver_internal.h"
|
||||
|
||||
#ifndef DEBUG_GOOSE_SUBSCRIBER
|
||||
#define DEBUG_GOOSE_SUBSCRIBER 0
|
||||
#endif
|
||||
|
||||
#define ETH_BUFFER_LENGTH 1518
|
||||
|
||||
#define ETH_P_GOOSE 0x88b8
|
||||
|
||||
struct sGooseReceiver {
|
||||
bool running;
|
||||
char* interfaceId;
|
||||
uint8_t* buffer;
|
||||
EthernetSocket ethSocket;
|
||||
LinkedList subscriberList;
|
||||
};
|
||||
|
||||
|
||||
GooseReceiver
|
||||
GooseReceiver_create()
|
||||
{
|
||||
GooseReceiver self = (GooseReceiver) GLOBAL_MALLOC(sizeof(struct sGooseReceiver));
|
||||
|
||||
if (self != NULL) {
|
||||
self->running = false;
|
||||
self->interfaceId = NULL;
|
||||
self->buffer = (uint8_t*) GLOBAL_MALLOC(ETH_BUFFER_LENGTH);
|
||||
self->ethSocket = NULL;
|
||||
self->subscriberList = LinkedList_create();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
GooseReceiver_addSubscriber(GooseReceiver self, GooseSubscriber subscriber)
|
||||
{
|
||||
LinkedList_add(self->subscriberList, (void*) subscriber);
|
||||
}
|
||||
|
||||
void
|
||||
GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber)
|
||||
{
|
||||
LinkedList_remove(self->subscriberList, (void*) subscriber);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId)
|
||||
{
|
||||
if (self->interfaceId != NULL)
|
||||
GLOBAL_FREEMEM(self->interfaceId);
|
||||
|
||||
self->interfaceId = copyString(interfaceId);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GooseReceiver_setBackupListener(GooseReceiver self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength)
|
||||
{
|
||||
value->value.visibleString.buf = (char*) GLOBAL_MALLOC(elementLength + 1);
|
||||
memcpy(value->value.visibleString.buf, bufferSrc, elementLength);
|
||||
value->value.visibleString.buf[elementLength] = 0;
|
||||
value->value.visibleString.size = elementLength;
|
||||
}
|
||||
|
||||
static int
|
||||
parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
|
||||
{
|
||||
int bufPos = 0;
|
||||
int elementLength = 0;
|
||||
|
||||
int elementIndex = 0;
|
||||
|
||||
int maxIndex = MmsValue_getArraySize(dataSetValues) - 1;
|
||||
|
||||
while (bufPos < allDataLength) {
|
||||
uint8_t tag = buffer[bufPos++];
|
||||
|
||||
if (elementIndex > maxIndex) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: too much elements!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex);
|
||||
|
||||
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
|
||||
|
||||
if (bufPos + elementLength > allDataLength) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case 0x80: /* reserved for access result */
|
||||
printf("GOOSE_SUBSCRIBER: found reserved value (tag 0x80)!\n");
|
||||
break;
|
||||
case 0xa1: /* array */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n");
|
||||
if (MmsValue_getType(value) == MMS_ARRAY) {
|
||||
if (!parseAllData(buffer + bufPos, elementLength, value))
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 0xa2: /* structure */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n");
|
||||
if (MmsValue_getType(value) == MMS_STRUCTURE) {
|
||||
if (!parseAllData(buffer + bufPos, elementLength, value))
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 0x83: /* boolean */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n");
|
||||
|
||||
if (MmsValue_getType(value) == MMS_BOOLEAN) {
|
||||
MmsValue_setBoolean(value, BerDecoder_decodeBoolean(buffer, bufPos));
|
||||
}
|
||||
else
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n");
|
||||
|
||||
break;
|
||||
|
||||
case 0x84: /* BIT STRING */
|
||||
if (MmsValue_getType(value) == MMS_BIT_STRING) {
|
||||
int padding = buffer[bufPos];
|
||||
int bitStringLength = (8 * (elementLength - 1)) - padding;
|
||||
if (bitStringLength == value->value.bitString.size) {
|
||||
memcpy(value->value.bitString.buf, buffer + bufPos + 1,
|
||||
elementLength - 1);
|
||||
}
|
||||
else
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("bit-string is of wrong size");
|
||||
}
|
||||
break;
|
||||
case 0x85: /* integer */
|
||||
if (MmsValue_getType(value) == MMS_INTEGER) {
|
||||
if (elementLength <= value->value.integer->maxSize) {
|
||||
value->value.integer->size = elementLength;
|
||||
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x86: /* unsigned integer */
|
||||
if (MmsValue_getType(value) == MMS_UNSIGNED) {
|
||||
if (elementLength <= value->value.integer->maxSize) {
|
||||
value->value.integer->size = elementLength;
|
||||
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x87: /* Float */
|
||||
if (MmsValue_getType(value) == MMS_FLOAT) {
|
||||
if (elementLength == 9) {
|
||||
MmsValue_setDouble(value, BerDecoder_decodeDouble(buffer, bufPos));
|
||||
}
|
||||
else if (elementLength == 5) {
|
||||
MmsValue_setFloat(value, BerDecoder_decodeFloat(buffer, bufPos));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x89: /* octet string */
|
||||
if (MmsValue_getType(value) == MMS_OCTET_STRING) {
|
||||
if (elementLength <= value->value.octetString.maxSize) {
|
||||
value->value.octetString.size = elementLength;
|
||||
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x8a: /* visible string */
|
||||
if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
|
||||
|
||||
if (value->value.visibleString.buf != NULL) {
|
||||
if ((int32_t) value->value.visibleString.size >= elementLength) {
|
||||
memcpy(value->value.visibleString.buf, buffer + bufPos, elementLength);
|
||||
value->value.visibleString.buf[elementLength] = 0;
|
||||
}
|
||||
else {
|
||||
GLOBAL_FREEMEM(value->value.visibleString.buf);
|
||||
|
||||
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
createNewStringFromBufferElement(value, buffer + bufPos, elementLength);
|
||||
|
||||
}
|
||||
break;
|
||||
case 0x8c: /* binary time */
|
||||
if (MmsValue_getType(value) == MMS_BINARY_TIME) {
|
||||
if ((elementLength == 4) || (elementLength == 6)) {
|
||||
memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x91: /* Utctime */
|
||||
if (elementLength == 8) {
|
||||
if (MmsValue_getType(value) == MMS_UTC_TIME) {
|
||||
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
|
||||
}
|
||||
else
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: message contains value of wrong type!\n");
|
||||
}
|
||||
else
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
|
||||
break;
|
||||
default:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
|
||||
break;
|
||||
}
|
||||
|
||||
bufPos += elementLength;
|
||||
|
||||
elementIndex++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static MmsValue*
|
||||
parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLength, bool isStructure)
|
||||
{
|
||||
int bufPos = 0;
|
||||
int elementLength = 0;
|
||||
|
||||
int elementIndex = 0;
|
||||
|
||||
MmsValue* dataSetValues = NULL;
|
||||
|
||||
while (bufPos < allDataLength) {
|
||||
uint8_t tag = buffer[bufPos++];
|
||||
|
||||
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
|
||||
|
||||
if (bufPos + elementLength > allDataLength) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
|
||||
goto exit_with_error;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case 0x80: /* reserved for access result */
|
||||
break;
|
||||
case 0xa1: /* array */
|
||||
break;
|
||||
case 0xa2: /* structure */
|
||||
break;
|
||||
case 0x83: /* boolean */
|
||||
break;
|
||||
case 0x84: /* BIT STRING */
|
||||
break;
|
||||
case 0x85: /* integer */
|
||||
break;
|
||||
case 0x86: /* unsigned integer */
|
||||
break;
|
||||
case 0x87: /* Float */
|
||||
break;
|
||||
case 0x89: /* octet string */
|
||||
break;
|
||||
case 0x8a: /* visible string */
|
||||
break;
|
||||
case 0x8c: /* binary time */
|
||||
break;
|
||||
case 0x91: /* Utctime */
|
||||
break;
|
||||
default:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
|
||||
goto exit_with_error;
|
||||
}
|
||||
|
||||
bufPos += elementLength;
|
||||
|
||||
elementIndex++;
|
||||
}
|
||||
|
||||
if (isStructure)
|
||||
dataSetValues = MmsValue_createEmptyStructure(elementIndex);
|
||||
else
|
||||
dataSetValues = MmsValue_createEmtpyArray(elementIndex);
|
||||
|
||||
elementIndex = 0;
|
||||
bufPos = 0;
|
||||
|
||||
while (bufPos < allDataLength) {
|
||||
uint8_t tag = buffer[bufPos++];
|
||||
|
||||
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength);
|
||||
|
||||
if (bufPos + elementLength > allDataLength) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
|
||||
goto exit_with_error;
|
||||
}
|
||||
|
||||
MmsValue* value = NULL;
|
||||
|
||||
switch (tag) {
|
||||
case 0xa1: /* array */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n");
|
||||
|
||||
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, false);
|
||||
|
||||
if (value == NULL)
|
||||
goto exit_with_error;
|
||||
|
||||
break;
|
||||
case 0xa2: /* structure */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n");
|
||||
|
||||
value = parseAllDataUnknownValue(self, buffer + bufPos, elementLength, true);
|
||||
|
||||
if (value == NULL)
|
||||
goto exit_with_error;
|
||||
|
||||
break;
|
||||
case 0x83: /* boolean */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n");
|
||||
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
|
||||
|
||||
break;
|
||||
|
||||
case 0x84: /* BIT STRING */
|
||||
{
|
||||
int padding = buffer[bufPos];
|
||||
int bitStringLength = (8 * (elementLength - 1)) - padding;
|
||||
value = MmsValue_newBitString(bitStringLength);
|
||||
memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1);
|
||||
|
||||
}
|
||||
break;
|
||||
case 0x85: /* integer */
|
||||
value = MmsValue_newInteger(elementLength * 8);
|
||||
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
|
||||
break;
|
||||
case 0x86: /* unsigned integer */
|
||||
value = MmsValue_newUnsigned(elementLength * 8);
|
||||
memcpy(value->value.integer->octets, buffer + bufPos, elementLength);
|
||||
break;
|
||||
case 0x87: /* Float */
|
||||
if (elementLength == 9)
|
||||
value = MmsValue_newDouble(BerDecoder_decodeDouble(buffer, bufPos));
|
||||
else if (elementLength == 5)
|
||||
value = MmsValue_newFloat(BerDecoder_decodeFloat(buffer, bufPos));
|
||||
break;
|
||||
|
||||
case 0x89: /* octet string */
|
||||
value = MmsValue_newOctetString(elementLength, elementLength);
|
||||
memcpy(value->value.octetString.buf, buffer + bufPos, elementLength);
|
||||
break;
|
||||
case 0x8a: /* visible string */
|
||||
value = MmsValue_newVisibleStringFromByteArray(buffer + bufPos, elementLength);
|
||||
break;
|
||||
case 0x8c: /* binary time */
|
||||
if (elementLength == 4)
|
||||
value = MmsValue_newBinaryTime(true);
|
||||
else if (elementLength == 6)
|
||||
value = MmsValue_newBinaryTime(false);
|
||||
|
||||
if ((elementLength == 4) || (elementLength == 6))
|
||||
memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength);
|
||||
|
||||
break;
|
||||
case 0x91: /* Utctime */
|
||||
if (elementLength == 8) {
|
||||
value = MmsValue_newUtcTime(0);
|
||||
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
|
||||
}
|
||||
else
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n");
|
||||
break;
|
||||
default:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found unkown tag %02x\n", tag);
|
||||
goto exit_with_error;
|
||||
}
|
||||
|
||||
bufPos += elementLength;
|
||||
|
||||
if (value != NULL) {
|
||||
MmsValue_setElement(dataSetValues, elementIndex, value);
|
||||
elementIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
self->dataSetValuesSelfAllocated = true;
|
||||
|
||||
return dataSetValues;
|
||||
|
||||
exit_with_error:
|
||||
|
||||
if (dataSetValues != NULL)
|
||||
MmsValue_delete(dataSetValues);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
|
||||
{
|
||||
int bufPos = 0;
|
||||
uint32_t timeAllowedToLive = 0;
|
||||
uint32_t stNum = 0;
|
||||
uint32_t sqNum = 0;
|
||||
uint32_t confRev;
|
||||
bool simulation = false;
|
||||
bool ndsCom = false;
|
||||
GooseSubscriber matchingSubscriber = NULL;
|
||||
uint8_t* timestampBufPos = NULL;
|
||||
uint8_t* dataSetBufferAddress = NULL;
|
||||
int dataSetBufferLength = 0;
|
||||
|
||||
uint32_t numberOfDatSetEntries = 0;
|
||||
|
||||
if (buffer[bufPos++] == 0x61) {
|
||||
int gooseLength;
|
||||
bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength);
|
||||
|
||||
int gooseEnd = bufPos + gooseLength;
|
||||
|
||||
while (bufPos < gooseEnd) {
|
||||
int elementLength;
|
||||
|
||||
uint8_t tag = buffer[bufPos++];
|
||||
bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength);
|
||||
|
||||
if (bufPos + elementLength > apduLength) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("GOOSE_SUBSCRIBER: Malformed message: sub element is too large!\n");
|
||||
|
||||
goto exit_with_fault;
|
||||
}
|
||||
|
||||
if (bufPos == -1)
|
||||
goto exit_with_fault;
|
||||
|
||||
switch(tag) {
|
||||
case 0x80: /* gocbRef */
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found gocbRef\n");
|
||||
|
||||
{
|
||||
LinkedList element = LinkedList_getNext(self->subscriberList);
|
||||
|
||||
while (element != NULL) {
|
||||
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
|
||||
|
||||
if (subscriber->goCBRefLen == elementLength) {
|
||||
if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n");
|
||||
matchingSubscriber = subscriber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
element = LinkedList_getNext(element);
|
||||
}
|
||||
|
||||
if (matchingSubscriber == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x81: /* timeAllowedToLive */
|
||||
|
||||
timeAllowedToLive = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
|
||||
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found timeAllowedToLive %u\n", timeAllowedToLive);
|
||||
|
||||
break;
|
||||
|
||||
case 0x82:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found dataSet\n");
|
||||
break;
|
||||
|
||||
case 0x83:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found goId\n");
|
||||
break;
|
||||
|
||||
case 0x84:
|
||||
timestampBufPos = buffer + bufPos;
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found timestamp\n");
|
||||
break;
|
||||
|
||||
case 0x85:
|
||||
stNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found stNum: %u\n", stNum);
|
||||
break;
|
||||
|
||||
case 0x86:
|
||||
sqNum = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found sqNum: %u\n", sqNum);
|
||||
break;
|
||||
|
||||
case 0x87:
|
||||
simulation = BerDecoder_decodeBoolean(buffer, bufPos);
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found simulation: %i\n", simulation);
|
||||
break;
|
||||
|
||||
case 0x88:
|
||||
confRev = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found confRev: %u\n", confRev);
|
||||
break;
|
||||
|
||||
case 0x89:
|
||||
ndsCom = BerDecoder_decodeBoolean(buffer, bufPos);
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found ndsCom: %i\n", ndsCom);
|
||||
break;
|
||||
|
||||
case 0x8a:
|
||||
numberOfDatSetEntries = BerDecoder_decodeUint32(buffer, elementLength, bufPos);
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found number of entries: %u\n", numberOfDatSetEntries);
|
||||
break;
|
||||
|
||||
case 0xab:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found all data with length: %i\n", elementLength);
|
||||
dataSetBufferAddress = buffer + bufPos;
|
||||
dataSetBufferLength = elementLength;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Unknown tag %02x\n", tag);
|
||||
break;
|
||||
}
|
||||
|
||||
bufPos += elementLength;
|
||||
}
|
||||
|
||||
if (matchingSubscriber != NULL) {
|
||||
|
||||
matchingSubscriber->timeAllowedToLive = timeAllowedToLive;
|
||||
matchingSubscriber->confRev = confRev;
|
||||
matchingSubscriber->ndsCom = ndsCom;
|
||||
matchingSubscriber->simulation = simulation;
|
||||
MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos);
|
||||
|
||||
if (matchingSubscriber->dataSetValues == NULL)
|
||||
matchingSubscriber->dataSetValues = parseAllDataUnknownValue(matchingSubscriber, dataSetBufferAddress, dataSetBufferLength, false);
|
||||
else
|
||||
parseAllData(dataSetBufferAddress, dataSetBufferLength, matchingSubscriber->dataSetValues);
|
||||
|
||||
bool isValid = true;
|
||||
|
||||
if (matchingSubscriber->stNum == stNum) {
|
||||
if (matchingSubscriber->sqNum >= sqNum) {
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
matchingSubscriber->stateValid = isValid;
|
||||
|
||||
matchingSubscriber->stNum = stNum;
|
||||
matchingSubscriber->sqNum = sqNum;
|
||||
|
||||
matchingSubscriber->invalidityTime = Hal_getTimeInMs() + timeAllowedToLive;
|
||||
|
||||
if (matchingSubscriber->listener != NULL)
|
||||
matchingSubscriber->listener(matchingSubscriber, matchingSubscriber->listenerParameter);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
exit_with_fault:
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Invalid goose payload\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parseGooseMessage(GooseReceiver self, int numbytes)
|
||||
{
|
||||
int bufPos;
|
||||
bool subscriberFound = false;
|
||||
uint8_t* buffer = self->buffer;
|
||||
|
||||
if (numbytes < 22) return;
|
||||
|
||||
/* skip ethernet addresses */
|
||||
bufPos = 12;
|
||||
int headerLength = 14;
|
||||
|
||||
/* check for VLAN tag */
|
||||
if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) {
|
||||
bufPos += 4; /* skip VLAN tag */
|
||||
headerLength += 4;
|
||||
}
|
||||
|
||||
/* check for GOOSE Ethertype */
|
||||
if (buffer[bufPos++] != 0x88)
|
||||
return;
|
||||
if (buffer[bufPos++] != 0xb8)
|
||||
return;
|
||||
|
||||
uint16_t appId;
|
||||
|
||||
appId = buffer[bufPos++] * 0x100;
|
||||
appId += buffer[bufPos++];
|
||||
|
||||
uint16_t length;
|
||||
|
||||
length = buffer[bufPos++] * 0x100;
|
||||
length += buffer[bufPos++];
|
||||
|
||||
/* skip reserved fields */
|
||||
bufPos += 4;
|
||||
|
||||
int apduLength = length - 8;
|
||||
|
||||
if (numbytes != length + headerLength) {
|
||||
if (DEBUG)
|
||||
printf("GOOSE_SUBSCRIBER: Invalid PDU size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG_GOOSE_SUBSCRIBER) {
|
||||
printf("GOOSE_SUBSCRIBER: GOOSE message:\nGOOSE_SUBSCRIBER: ----------------\n");
|
||||
printf("GOOSE_SUBSCRIBER: APPID: %u\n", appId);
|
||||
printf("GOOSE_SUBSCRIBER: LENGTH: %u\n", length);
|
||||
printf("GOOSE_SUBSCRIBER: APDU length: %i\n", apduLength);
|
||||
}
|
||||
|
||||
|
||||
// check if there is an interested subscriber
|
||||
LinkedList element = LinkedList_getNext(self->subscriberList);
|
||||
|
||||
while (element != NULL) {
|
||||
GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element);
|
||||
|
||||
if (subscriber->appId == appId) {
|
||||
subscriberFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
element = LinkedList_getNext(element);
|
||||
}
|
||||
|
||||
if (subscriberFound)
|
||||
parseGoosePayload(self, buffer + bufPos, apduLength);
|
||||
else {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("GOOSE_SUBSCRIBER: GOOSE message ignored due to unknown APPID value\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gooseReceiverLoop(void* threadParameter)
|
||||
{
|
||||
GooseReceiver self = (GooseReceiver) threadParameter;
|
||||
|
||||
GooseReceiver_startThreadless(self);
|
||||
|
||||
while (self->running) {
|
||||
|
||||
GooseReceiver_tick(self);
|
||||
|
||||
Thread_sleep(1);
|
||||
}
|
||||
|
||||
GooseReceiver_stopThreadless(self);
|
||||
}
|
||||
|
||||
|
||||
// start GOOSE receiver in a separate thread
|
||||
void
|
||||
GooseReceiver_start(GooseReceiver self)
|
||||
{
|
||||
Thread thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, true);
|
||||
|
||||
if (thread != NULL) {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("GOOSE_SUBSCRIBER: GOOSE receiver started for interface %s\n", self->interfaceId);
|
||||
|
||||
Thread_start(thread);
|
||||
}
|
||||
else {
|
||||
if (DEBUG_GOOSE_SUBSCRIBER)
|
||||
printf("GOOSE_SUBSCRIBER: Starting GOOSE receiver failed for interface %s\n", self->interfaceId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
GooseReceiver_stop(GooseReceiver self)
|
||||
{
|
||||
self->running = false;
|
||||
}
|
||||
|
||||
void
|
||||
GooseReceiver_destroy(GooseReceiver self)
|
||||
{
|
||||
LinkedList_destroyDeep(self->subscriberList,
|
||||
(LinkedListValueDeleteFunction) GooseSubscriber_destroy);
|
||||
|
||||
GLOBAL_FREEMEM(self->buffer);
|
||||
GLOBAL_FREEMEM(self);
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Functions for non-threaded operation
|
||||
***************************************/
|
||||
void
|
||||
GooseReceiver_startThreadless(GooseReceiver self)
|
||||
{
|
||||
|
||||
if (self->interfaceId == NULL)
|
||||
self->ethSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, NULL);
|
||||
else
|
||||
self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL);
|
||||
|
||||
Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE);
|
||||
|
||||
self->running = true;
|
||||
}
|
||||
|
||||
void
|
||||
GooseReceiver_stopThreadless(GooseReceiver self)
|
||||
{
|
||||
Ethernet_destroySocket(self->ethSocket);
|
||||
|
||||
self->running = false;
|
||||
}
|
||||
|
||||
// call after reception of ethernet frame and periodically to to house keeping tasks
|
||||
void
|
||||
GooseReceiver_tick(GooseReceiver self)
|
||||
{
|
||||
int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH);
|
||||
|
||||
if (packetSize > 0)
|
||||
parseGooseMessage(self, packetSize);
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* goose_receiver.h
|
||||
*
|
||||
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#ifndef GOOSE_RECEIVER_H_
|
||||
#define GOOSE_RECEIVER_H_
|
||||
|
||||
#include <goose_subscriber.h>
|
||||
|
||||
typedef struct sGooseReceiver* GooseReceiver;
|
||||
|
||||
GooseReceiver
|
||||
GooseReceiver_create(void);
|
||||
|
||||
void
|
||||
GooseReceiver_setInterfaceId(GooseReceiver self, const char* interfaceId);
|
||||
|
||||
void
|
||||
GooseReceiver_addSubscriber(GooseReceiver self, GooseSubscriber subscriber);
|
||||
|
||||
void
|
||||
GooseReceiver_removeSubscriber(GooseReceiver self, GooseSubscriber subscriber);
|
||||
|
||||
// call backup listener if message is not handled by a subscriber
|
||||
void
|
||||
GooseReceiver_setBackupListener(GooseReceiver self);
|
||||
|
||||
// start GOOSE receiver in a separate thread
|
||||
void
|
||||
GooseReceiver_start(GooseReceiver self);
|
||||
|
||||
void
|
||||
GooseReceiver_stop(GooseReceiver self);
|
||||
|
||||
void
|
||||
GooseReceiver_destroy(GooseReceiver self);
|
||||
|
||||
/***************************************
|
||||
* Functions for non-threaded operation
|
||||
***************************************/
|
||||
void
|
||||
GooseReceiver_startThreadless(GooseReceiver self);
|
||||
|
||||
void
|
||||
GooseReceiver_stopThreadless(GooseReceiver self);
|
||||
|
||||
// call after reception of ethernet frame and periodically to to house keeping tasks
|
||||
void
|
||||
GooseReceiver_tick(GooseReceiver self);
|
||||
|
||||
#endif /* GOOSE_RECEIVER_H_ */
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* goose_receiver_internal.h
|
||||
*
|
||||
* Copyright 2014 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#ifndef GOOSE_RECEIVER_INTERNAL_H_
|
||||
#define GOOSE_RECEIVER_INTERNAL_H_
|
||||
|
||||
|
||||
#define ETH_BUFFER_LENGTH 1518
|
||||
|
||||
#define ETH_P_GOOSE 0x88b8
|
||||
|
||||
#ifndef DEBUG_GOOSE_SUBSCRIBER
|
||||
#define DEBUG_GOOSE_SUBSCRIBER 0
|
||||
#endif
|
||||
|
||||
|
||||
struct sGooseSubscriber {
|
||||
char* goCBRef;
|
||||
int goCBRefLen;
|
||||
uint32_t timeAllowedToLive;
|
||||
uint32_t stNum;
|
||||
uint32_t sqNum;
|
||||
uint32_t confRev;
|
||||
MmsValue* timestamp;
|
||||
bool simulation;
|
||||
bool ndsCom;
|
||||
|
||||
uint64_t invalidityTime;
|
||||
bool stateValid;
|
||||
|
||||
int32_t appId; /* APPID or -1 if APPID should be ignored */
|
||||
|
||||
MmsValue* dataSetValues;
|
||||
bool dataSetValuesSelfAllocated;
|
||||
|
||||
GooseListener listener;
|
||||
void* listenerParameter;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* GOOSE_RECEIVER_INTERNAL_H_ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue