Compare commits

...

15 Commits
v1.5 ... 1.1.2

@ -1,3 +1,35 @@
Changes to version 1.1.2
------------------------
- MMS client: fixed parsing initiate response message
- SV publisher: conditional encoding for SmpRate
- MmsValue_update function now allows adjusting octet-string size of target object
- .NET API: Added DeleteFile
- CDC helper functions: added helper functions for VSS and VSG CDC
- added additional locks in client and server
Changes to version 1.1.1
------------------------
- IEC 61850 client: fixed bug in APC control handling
- IEC 61850 client: ClientReportControlBlock now accepts "$" and "." as seperator for RCB object reference
- MMS client: fixed bug in MmsConnection_connect (COTP payload buffer was not reset in case of an error during connect -> connection failed in case of reuse of MmsConnection object
- MMS client: delete named variable list service supports VMD specific lists
- SV subscriber/publisher: additional features and bug fixes
- SV: fixed data type for smpRate
- SV: fixed encoding of optional smpMod attribute
- SV receiver: Added semaphore to make subscriber list thread-safe
- .NET API: ControlObject implements IDisposable interface
- IED server: added new function IedServer_udpateDbposValue
- fixed problem with cmake include folders
- MMS client: file services -fixed encoding problem with long file names
- MMS server: ACSE authenticator passes application reference (ap-title and ae-qualifier)
- example directory cleanup
- MMS: fixed potential memory leak in asn1 code that can be caused by malformed MMS messages
- MMS client: MmsConnection_getVariableAccessAttributes support for VMD specific variables
- Java SCL parser: added support for "Val" elements for Octet64 types
Changes to version 1.1.0
------------------------

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

@ -28,7 +28,7 @@
#define DEBUG_HAL_ETHERNET 0
/* Maximum MMS PDU SIZE - default is 65000 */
#define CONFIG_MMS_MAXIMUM_PDU_SIZE 120000
#define CONFIG_MMS_MAXIMUM_PDU_SIZE 65000
/*
* Enable single threaded mode
@ -37,7 +37,7 @@
* 0 ==> server runs in multi-threaded mode (one thread for each connection and
* one server background thread )
*/
#define CONFIG_MMS_SINGLE_THREADED 0
#define CONFIG_MMS_SINGLE_THREADED 1
/*
* Optimize stack for threadless operation - don't use semaphores

@ -17,7 +17,7 @@ using System.Runtime.CompilerServices;
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.2")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.

@ -1035,6 +1035,18 @@ namespace IEC61850
if (error != 0)
throw new IedConnectionException ("Write value failed", error);
}
/// <summary>Delete file</summary>
/// <param name="fileName">The name of the file.</param>
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public void DeleteFile (string fileName)
{
int error;
IedConnection_deleteFile (connection, out error, fileName);
if (error != 0)
throw new IedConnectionException ("Deleting file " + fileName + " failed", error);
}
/// <summary>Read the content of a file directory.</summary>

@ -50,8 +50,6 @@ int main(int argc, char** argv) {
if (error == IED_ERROR_OK) {
IedConnection_getServerDirectory(con, &error, false);
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);

@ -15,7 +15,7 @@
#include "platform_endian.h"
#define LIBIEC61850_VERSION "1.1.0"
#define LIBIEC61850_VERSION "1.1.2"
#ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"

@ -18,7 +18,7 @@ DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "libIEC61850"
PROJECT_NUMBER = 1.1
PROJECT_NUMBER = 1.1.2
PROJECT_BRIEF = "Open-source IEC 61850 MMS/GOOSE/SV server and client library"

@ -149,6 +149,7 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
bool hasOper = false;
bool hasTimeActivatedControl = false;
bool hasCtlNum = false;
bool isAPC = false;
MmsVariableSpecification* ctlVal = NULL;
MmsVariableSpecification* t = NULL;
@ -157,7 +158,12 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
if (oper)
{
hasOper = true;
hasOper = true;
ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlVal");
if (MmsVariableSpecification_getType(ctlVal) == MMS_STRUCTURE)
isAPC = true;
MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm");
@ -169,7 +175,6 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
if (ctlNum)
hasCtlNum = true;
ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlVal");
t = MmsVariableSpecification_getNamedVariableRecursive(oper, "T");
}
}
@ -200,6 +205,11 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
self->hasCtlNum = hasCtlNum;
self->ctlVal = MmsValue_newDefaultValue(ctlVal);
if (isAPC)
self->analogValue = MmsValue_createEmptyStructure(1);
else
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;
@ -218,162 +228,6 @@ exit_function:
return self;
}
#if 0
ControlObjectClient
ControlObjectClient_create(const char* objectReference, IedConnection connection)
{
ControlObjectClient self = NULL;
/* request control model from server */
char domainId[65];
char itemId[129];
char* domainName = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainId);
if (domainName == NULL)
goto exit_function;
convertToMmsAndInsertFC(itemId, objectReference + strlen(domainId) + 1, "CF");
int controlObjectItemIdLen = strlen(itemId);
strncat(itemId, "$ctlModel", 64 - controlObjectItemIdLen);
MmsError mmsError;
MmsValue* ctlModel = MmsConnection_readVariable(IedConnection_getMmsConnection(connection),
&mmsError, domainId, itemId);
if (ctlModel == NULL) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ControlObjectClient_create: failed to get ctlModel from server\n");
goto exit_function;
}
int ctlModelVal = MmsValue_toUint32(ctlModel);
MmsValue_delete(ctlModel);
IedClientError error;
LinkedList dataDirectory =
IedConnection_getDataDirectory(connection, &error, objectReference);
if (dataDirectory == NULL) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ControlObjectClient_create: failed to get data directory of control object\n");
goto exit_function;
}
/* check what control elements are available */
bool hasOper = false;
LinkedList element = LinkedList_getNext(dataDirectory);
while (element != NULL) {
char* objectName = (char*) element->data;
if (strcmp(objectName, "Oper") == 0)
hasOper = true;
element = LinkedList_getNext(element);
}
LinkedList_destroy(dataDirectory);
if (hasOper == false) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: control is missing required element \"Oper\"\n");
goto exit_function;
}
/* check for time activated control and ctlNum */
bool hasTimeActivatedControl = false;
bool hasCtlNum = false;
strcpy(itemId, objectReference);
strcat(itemId, ".Oper");
dataDirectory = IedConnection_getDataDirectory(connection, &error, itemId);
if (dataDirectory == NULL)
goto exit_function;
element = LinkedList_getNext(dataDirectory);
while (element != NULL) {
char* objectName = (char*) element->data;
if (strcmp(objectName, "operTm") == 0) {
hasTimeActivatedControl = true;
}
else if (strcmp(objectName, "ctlNum") == 0) {
hasCtlNum = true;
}
element = LinkedList_getNext(element);
}
LinkedList_destroy(dataDirectory);
/* get default parameters for Oper control variable */
MmsValue* oper = IedConnection_readObject(connection, &error, itemId, IEC61850_FC_CO);
if (oper == NULL) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: reading \"Oper\" failed!\n");
goto exit_function;
}
self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient));
if (self == NULL)
goto exit_function;
self->objectReference = StringUtils_copyString(objectReference);
self->connection = connection;
self->ctlModel = (ControlModel) ctlModelVal;
self->hasTimeActivatedMode = hasTimeActivatedControl;
self->hasCtlNum = hasCtlNum;
self->ctlVal = MmsValue_getElement(oper, 0);
if (MmsValue_getType(self->ctlVal) == MMS_STRUCTURE)
self->analogValue = MmsValue_createEmptyStructure(1);
else
self->analogValue = NULL;
/* Check for T element type (Binary time -> Ed.1,UTC time -> Ed.2) */
MmsValue* t;
if (hasTimeActivatedControl)
t = MmsValue_getElement(oper, 4);
else
t = MmsValue_getElement(oper, 3);
if (MmsValue_getType(t) == MMS_BINARY_TIME)
self->edition = 1;
else
self->edition = 2;
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: Detected edition %i control\n", self->edition);
MmsValue_setElement(oper, 0, NULL);
MmsValue_delete(oper);
private_IedConnection_addControlClient(connection, self);
exit_function:
return self;
}
#endif
void
ControlObjectClient_destroy(ControlObjectClient self)
{

@ -32,12 +32,19 @@
#include "libiec61850_platform_includes.h"
static bool
isBufferedRcb(const char* objectReference)
isBufferedRcb(const char* objectReference, bool* isValid)
{
const char* separator = strchr(objectReference, '.');
if (separator == NULL)
separator = strchr(objectReference, '$');
if (separator == NULL) {
*isValid = false;
return false;
}
*isValid = true;
if (*(separator + 1) == 'B')
return true;
@ -48,12 +55,24 @@ isBufferedRcb(const char* objectReference)
ClientReportControlBlock
ClientReportControlBlock_create(const char* objectReference)
{
ClientReportControlBlock self = (ClientReportControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientReportControlBlock));
bool isReferenceValid;
bool isBuffered;
isBuffered = isBufferedRcb(objectReference, &isReferenceValid);
if (isReferenceValid == false) {
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: RCB reference invalid\n");
return NULL;
}
//TODO check validity of object reference?!
ClientReportControlBlock self = (ClientReportControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientReportControlBlock));
self->objectReference = StringUtils_copyString(objectReference);
self->isBuffered = isBufferedRcb(objectReference);
if (self) {
self->objectReference = StringUtils_copyString(objectReference);
self->isBuffered = isBuffered;
}
return self;
}

@ -184,7 +184,8 @@ CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
DataObject*
CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
DataObject*
CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
/**
* \brief create a new SEC (Security violation) CDC instance (data object)
@ -340,6 +341,16 @@ CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
DataObject*
CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
/**
* \brief Visible string setting (VSG)
*
* \param dataObjectName the name of the new object
* \param parent the parent of the new data object (either a LogicalNode or another DataObject)
* \param options bit mask to encode required optional elements
*/
DataObject*
CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options);
/**
* \brief Enumerated status setting (ENG)
*

@ -379,7 +379,23 @@ CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options)
}
DataObject*
CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options)
{
DataObject* newSPS = DataObject_create(dataObjectName, parent, 0);
CDC_addStatusToDataObject(newSPS, IEC61850_VISIBLE_STRING_255);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubst(newSPS, IEC61850_BOOLEAN);
if (options & CDC_OPTION_BLK_ENA)
DataAttribute_create("blkEna", (ModelNode*) newSPS, IEC61850_BOOLEAN, IEC61850_FC_BL, 0, 0, 0);
CDC_addStandardOptions(newSPS, options);
return newSPS;
}
/**
* CDC_OPTION_INST_MAG
@ -931,6 +947,19 @@ CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options)
return newSPG;
}
DataObject*
CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options)
{
DataObject* newSPG = DataObject_create(dataObjectName, parent, 0);
DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_VISIBLE_STRING_255, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0);
CDC_addStandardOptions(newSPG, options);
return newSPG;
}
DataObject*
CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options)
{

@ -79,8 +79,12 @@ struct sMmsConnection {
uint32_t connectTimeout;
IsoClientConnection isoClient;
AssociationState associationState;
ConnectionState connectionState;
volatile AssociationState associationState;
Semaphore associationStateLock;
volatile ConnectionState connectionState;
Semaphore connectionStateLock;
MmsConnectionParameters parameters;
IsoConnectionParameters isoParameters;
@ -97,7 +101,8 @@ struct sMmsConnection {
#endif
/* state of an active connection conclude/release process */
int concludeState;
volatile int concludeState;
Semaphore concludeStateLock;
#if (MMS_OBTAIN_FILE_SERVICE == 1)
int32_t nextFrsmId;

@ -55,7 +55,10 @@ struct sIsoClientConnection
{
IsoIndicationCallback callback;
void* callbackParameter;
volatile int state;
Semaphore stateMutex;
Socket socket;
CotpConnection* cotpConnection;
IsoPresentation* presentation;
@ -86,13 +89,32 @@ struct sIsoClientConnection
Thread thread;
};
static void
setState(IsoClientConnection self, int newState)
{
Semaphore_wait(self->stateMutex);
self->state = newState;
Semaphore_post(self->stateMutex);
}
static int
getState(IsoClientConnection self)
{
int stateVal;
Semaphore_wait(self->stateMutex);
stateVal = self->state;
Semaphore_post(self->stateMutex);
return stateVal;
}
static void
connectionHandlingThread(IsoClientConnection self)
{
IsoSessionIndication sessionIndication;
self->handlingThreadRunning = true;
self->stopHandlingThread = false;
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT_CONNECTION: new connection %p\n", self);
@ -157,7 +179,7 @@ connectionHandlingThread(IsoClientConnection self)
self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL);;
self->state = STATE_IDLE;
setState(self, STATE_IDLE);
Socket_destroy(self->socket);
@ -202,7 +224,9 @@ IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParamet
self->callback = callback;
self->callbackParameter = callbackParameter;
self->state = STATE_IDLE;
self->stateMutex = Semaphore_create(1);
self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(ISO_CLIENT_BUFFER_SIZE);
@ -235,6 +259,12 @@ IsoClientConnection_create(IsoIndicationCallback callback, void* callbackParamet
self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection));
self->handlingThreadRunning = false;
self->stopHandlingThread = false;
self->destroyHandlingThread = false;
self->startHandlingThread = false;
return self;
}
@ -361,7 +391,7 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters
/* wait for upper layer to release buffer */
Semaphore_wait(self->receiveBufferMutex);
self->state = STATE_ASSOCIATED;
setState(self, STATE_ASSOCIATED);
if (self->thread == NULL) {
self->thread = Thread_create(connectionThreadFunction, self, false);
@ -378,7 +408,7 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters
returnError:
self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL);
self->state = STATE_ERROR;
setState(self, STATE_ERROR);
Socket_destroy(self->socket);
self->socket = NULL;
@ -432,7 +462,7 @@ IsoClientConnection_close(IsoClientConnection self)
Thread_sleep(1);
}
self->state = STATE_IDLE;
setState(self, STATE_IDLE);
}
@ -442,7 +472,7 @@ IsoClientConnection_destroy(IsoClientConnection self)
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: IsoClientConnection_destroy\n");
if (self->state == STATE_ASSOCIATED) {
if (getState(self) == STATE_ASSOCIATED) {
if (DEBUG_ISO_CLIENT)
printf("ISO_CLIENT: call IsoClientConnection_close\n");
@ -489,6 +519,7 @@ IsoClientConnection_destroy(IsoClientConnection self)
Semaphore_destroy(self->receiveBufferMutex);
Semaphore_destroy(self->transmitBufferMutex);
Semaphore_destroy(self->stateMutex);
GLOBAL_FREEMEM(self->sendBuffer);
GLOBAL_FREEMEM(self);

@ -39,6 +39,66 @@
#define CONFIG_MMS_CONNECTION_DEFAULT_CONNECT_TIMEOUT 10000
#define OUTSTANDING_CALLS 10
static void
setAssociationState(MmsConnection self, AssociationState newState)
{
Semaphore_wait(self->associationStateLock);
self->associationState = newState;
Semaphore_post(self->associationStateLock);
}
static AssociationState
getAssociationState(MmsConnection self)
{
AssociationState state;
Semaphore_wait(self->associationStateLock);
state = self->associationState;
Semaphore_post(self->associationStateLock);
return state;
}
static void
setConnectionState(MmsConnection self, ConnectionState newState)
{
Semaphore_wait(self->connectionStateLock);
self->connectionState = newState;
Semaphore_post(self->connectionStateLock);
}
static ConnectionState
getConnectionState(MmsConnection self)
{
ConnectionState state;
Semaphore_wait(self->connectionStateLock);
state = self->connectionState;
Semaphore_post(self->connectionStateLock);
return state;
}
static void
setConcludeState(MmsConnection self, int newState)
{
Semaphore_wait(self->concludeStateLock);
self->concludeState = newState;
Semaphore_post(self->concludeStateLock);
}
static int
getConcludeState(MmsConnection self)
{
int state;
Semaphore_wait(self->concludeStateLock);
state = self->concludeState;
Semaphore_post(self->concludeStateLock);
return state;
}
static void
handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message)
{
@ -310,7 +370,7 @@ sendRequestAndWaitForResponse(MmsConnection self, uint32_t invokeId, ByteBuffer*
while (currentTime < waitUntilTime) {
uint32_t receivedInvokeId;
if (self->associationState == MMS_STATE_CLOSED) {
if (getAssociationState(self) == MMS_STATE_CLOSED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto connection_lost;
}
@ -688,8 +748,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
if (indication == ISO_IND_CLOSED) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsIsoCallback: Connection lost or closed by client!\n");
self->connectionState = MMS_CON_IDLE;
self->associationState = MMS_STATE_CLOSED;
setConnectionState(self, MMS_CON_IDLE);
setAssociationState(self, MMS_STATE_CLOSED);
/* Call user provided callback function */
if (self->connectionLostHandler != NULL)
@ -701,8 +761,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
if (indication == ISO_IND_ASSOCIATION_FAILED) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsIsoCallback: association failed!\n");
self->connectionState = MMS_CON_ASSOCIATION_FAILED;
self->associationState = MMS_STATE_CLOSED;
setConnectionState(self, MMS_CON_ASSOCIATION_FAILED);
setAssociationState(self, MMS_STATE_CLOSED);
return;
}
@ -728,12 +788,12 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
printf("MMS_CLIENT: MMS-PDU: %02x\n", tag);
if (tag == 0xa9) { /* initiate response PDU */
if (indication == ISO_IND_ASSOCIATION_SUCCESS) {
self->connectionState = MMS_CON_ASSOCIATED;
}
else {
self->connectionState = MMS_CON_ASSOCIATION_FAILED;
}
if (indication == ISO_IND_ASSOCIATION_SUCCESS)
setConnectionState(self, MMS_CON_ASSOCIATED);
else
setConnectionState(self, MMS_CON_ASSOCIATION_FAILED);
self->lastResponse = payload;
IsoClientConnection_releaseReceiveBuffer(self->isoClient);
@ -746,7 +806,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: received conclude.request\n");
self->concludeState = CONCLUDE_STATE_REQUESTED;
setConcludeState(self, CONCLUDE_STATE_REQUESTED);
/* TODO block all new user requests? */
@ -756,7 +816,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: received conclude.reponse+\n");
self->concludeState = CONCLUDE_STATE_ACCEPTED;
setConcludeState(self, CONCLUDE_STATE_ACCEPTED);
IsoClientConnection_release(self->isoClient);
@ -766,7 +826,7 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: received conclude.reponse-\n");
self->concludeState = CONCLUDE_STATE_REJECTED;
setConcludeState(self, CONCLUDE_STATE_REJECTED);
IsoClientConnection_releaseReceiveBuffer(self->isoClient);
}
@ -1030,6 +1090,10 @@ MmsConnection_create()
self->lastResponseLock = Semaphore_create(1);
self->outstandingCallsLock = Semaphore_create(1);
self->connectionStateLock = Semaphore_create(1);
self->concludeStateLock = Semaphore_create(1);
self->associationStateLock = Semaphore_create(1);
self->lastResponseError = MMS_ERROR_NONE;
self->outstandingCalls = (uint32_t*) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(uint32_t));
@ -1065,6 +1129,10 @@ MmsConnection_destroy(MmsConnection self)
Semaphore_destroy(self->lastResponseLock);
Semaphore_destroy(self->outstandingCallsLock);
Semaphore_destroy(self->associationStateLock);
Semaphore_destroy(self->connectionStateLock);
Semaphore_destroy(self->concludeStateLock);
GLOBAL_FREEMEM(self->outstandingCalls);
#if (MMS_OBTAIN_FILE_SERVICE == 1)
@ -1172,7 +1240,7 @@ waitForConnectResponse(MmsConnection self)
uint64_t waitUntilTime = currentTime + self->requestTimeout;
while (currentTime < waitUntilTime) {
if (self->connectionState != MMS_CON_WAITING)
if (getConnectionState(self) != MMS_CON_WAITING)
break;
Thread_sleep(10);
@ -1200,7 +1268,7 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server
}
#endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */
self->connectionState = MMS_CON_WAITING;
setConnectionState(self, MMS_CON_WAITING);
IsoClientConnection_associate(self->isoClient, self->isoParameters, payload,
self->connectTimeout);
@ -1208,25 +1276,24 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server
waitForConnectResponse(self);
if (DEBUG_MMS_CLIENT)
printf("MmsConnection_connect: received response conState: %i\n", self->connectionState);
printf("MmsConnection_connect: received response conState: %i\n", getConnectionState(self));
if (self->connectionState == MMS_CON_ASSOCIATED) {
if (getConnectionState(self) == MMS_CON_ASSOCIATED) {
mmsClient_parseInitiateResponse(self);
releaseResponse(self);
self->associationState = MMS_STATE_CONNECTED;
}
else {
self->associationState = MMS_STATE_CLOSED;
setAssociationState(self, MMS_STATE_CONNECTED);
}
else
setAssociationState(self, MMS_STATE_CLOSED);
self->connectionState = MMS_CON_IDLE;
setConnectionState(self, MMS_CON_IDLE);
if (DEBUG_MMS_CLIENT)
printf("MmsConnection_connect: states: con %i ass %i\n", self->connectionState, self->associationState);
printf("MmsConnection_connect: states: con %i ass %i\n", getConnectionState(self), getAssociationState(self));
if (self->associationState == MMS_STATE_CONNECTED) {
if (getAssociationState(self) == MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_NONE;
return true;
}
@ -1241,7 +1308,7 @@ MmsConnection_close(MmsConnection self)
{
self->connectionLostHandler = NULL;
if (self->associationState == MMS_STATE_CONNECTED)
if (getAssociationState(self) == MMS_STATE_CONNECTED)
IsoClientConnection_close(self->isoClient);
}
@ -1254,7 +1321,7 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError)
bool success = true;
if (self->associationState == MMS_STATE_CONNECTED)
if (getAssociationState(self) == MMS_STATE_CONNECTED)
success = IsoClientConnection_abort(self->isoClient);
if (success == false) {
@ -1278,16 +1345,16 @@ sendConcludeRequestAndWaitForResponse(MmsConnection self)
mmsClient_createConcludeRequest(self, concludeMessage);
self->concludeState = CONCLUDE_STATE_REQUESTED;
setConcludeState(self, CONCLUDE_STATE_REQUESTED);
IsoClientConnection_sendMessage(self->isoClient, concludeMessage);
while (currentTime < waitUntilTime) {
if (self->associationState == MMS_STATE_CLOSED)
if (getAssociationState(self) == MMS_STATE_CLOSED)
goto exit_function;
if (self->concludeState != CONCLUDE_STATE_REQUESTED) {
if (getConcludeState(self) != CONCLUDE_STATE_REQUESTED) {
success = true;
break;
}
@ -1310,7 +1377,7 @@ exit_function:
void
MmsConnection_conclude(MmsConnection self, MmsError* mmsError)
{
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1324,12 +1391,12 @@ MmsConnection_conclude(MmsConnection self, MmsError* mmsError)
releaseResponse(self);
if (self->concludeState != CONCLUDE_STATE_ACCEPTED) {
if (getConcludeState(self) != CONCLUDE_STATE_ACCEPTED) {
if (self->associationState == MMS_STATE_CLOSED)
if (getAssociationState(self) == MMS_STATE_CLOSED)
*mmsError = MMS_ERROR_CONNECTION_LOST;
if (self->concludeState == CONCLUDE_STATE_REJECTED)
if (getConcludeState(self) == CONCLUDE_STATE_REJECTED)
*mmsError = MMS_ERROR_CONCLUDE_REJECTED;
}
@ -1359,7 +1426,7 @@ mmsClient_getNameListSingleRequest(
{
bool moreFollows = false;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1462,7 +1529,7 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError,
{
MmsValue* value = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1491,7 +1558,7 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError,
{
MmsValue* value = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1520,7 +1587,7 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError,
{
MmsValue* value = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1549,7 +1616,7 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError
{
MmsValue* value = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1580,7 +1647,7 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific(
{
MmsValue* value = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1609,7 +1676,7 @@ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsEr
{
LinkedList attributes = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1639,7 +1706,7 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection se
{
LinkedList attributes = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1667,7 +1734,7 @@ void
MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* listName, LinkedList variableSpecs)
{
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1695,7 +1762,7 @@ void
MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self,
MmsError* mmsError, const char* listName, LinkedList variableSpecs)
{
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1726,7 +1793,7 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError,
{
bool isDeleted = false;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1755,7 +1822,7 @@ MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self,
{
bool isDeleted = false;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1785,7 +1852,7 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError
{
MmsVariableSpecification* typeSpec = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
@ -1812,7 +1879,7 @@ MmsConnection_identify(MmsConnection self, MmsError* mmsError)
{
MmsServerIdentity* identity = NULL;
if (self->associationState != MMS_STATE_CONNECTED) {
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}

@ -170,20 +170,30 @@ mmsClient_parseInitiateResponse(MmsConnection self)
self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED;
self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING;
int bufPos = 0;
int bufPos = 1; /* ignore tag - already checked */
int maxBufPos = ByteBuffer_getSize(self->lastResponse);
uint8_t* buffer = ByteBuffer_getBuffer(self->lastResponse);
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0)
return false;
if (bufPos + length > maxBufPos)
return false;
while (bufPos < maxBufPos) {
uint8_t tag = buffer[bufPos++];
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0) {
// TODO write initiate error PDU!
if (bufPos < 0)
return false;
if (bufPos + length > maxBufPos)
return false;
}
switch (tag) {
case 0x80: /* local-detail-calling */

@ -223,67 +223,76 @@ MmsValue_equalTypes(const MmsValue* self, const MmsValue* otherValue)
bool
MmsValue_update(MmsValue* self, const MmsValue* update)
{
if (self->type == update->type) {
switch (self->type) {
case MMS_STRUCTURE:
case MMS_ARRAY:
if (updateStructuredComponent(self, update) == false)
return false;
break;
case MMS_BOOLEAN:
self->value.boolean = update->value.boolean;
break;
case MMS_FLOAT:
if (self->value.floatingPoint.formatWidth == update->value.floatingPoint.formatWidth) {
self->value.floatingPoint.exponentWidth = update->value.floatingPoint.exponentWidth;
memcpy(self->value.floatingPoint.buf, update->value.floatingPoint.buf,
self->value.floatingPoint.formatWidth / 8);
}
else return false;
break;
case MMS_INTEGER:
case MMS_UNSIGNED:
if (BerInteger_setFromBerInteger(self->value.integer, update->value.integer))
return true;
else
return false;
break;
case MMS_UTC_TIME:
memcpy(self->value.utcTime, update->value.utcTime, 8);
break;
case MMS_BIT_STRING:
if (self->value.bitString.size == update->value.bitString.size)
memcpy(self->value.bitString.buf, update->value.bitString.buf, bitStringByteSize(self));
else return false;
break;
case MMS_OCTET_STRING:
if (self->value.octetString.maxSize == update->value.octetString.maxSize) {
memcpy(self->value.octetString.buf, update->value.octetString.buf,
update->value.octetString.size);
self->value.octetString.size = update->value.octetString.size;
}
else return false;
break;
case MMS_VISIBLE_STRING:
MmsValue_setVisibleString(self, update->value.visibleString.buf);
break;
case MMS_STRING:
MmsValue_setMmsString(self, update->value.visibleString.buf);
break;
case MMS_BINARY_TIME:
self->value.binaryTime.size = update->value.binaryTime.size;
memcpy(self->value.binaryTime.buf, update->value.binaryTime.buf,
update->value.binaryTime.size);
break;
default:
return false;
break;
}
return true;
}
else
return false;
if (self->type == update->type) {
switch (self->type) {
case MMS_STRUCTURE:
case MMS_ARRAY:
if (updateStructuredComponent(self, update) == false)
return false;
break;
case MMS_BOOLEAN:
self->value.boolean = update->value.boolean;
break;
case MMS_FLOAT:
if (self->value.floatingPoint.formatWidth == update->value.floatingPoint.formatWidth) {
self->value.floatingPoint.exponentWidth = update->value.floatingPoint.exponentWidth;
memcpy(self->value.floatingPoint.buf, update->value.floatingPoint.buf,
self->value.floatingPoint.formatWidth / 8);
}
else
return false;
break;
case MMS_INTEGER:
case MMS_UNSIGNED:
if (BerInteger_setFromBerInteger(self->value.integer, update->value.integer))
return true;
else
return false;
break;
case MMS_UTC_TIME:
memcpy(self->value.utcTime, update->value.utcTime, 8);
break;
case MMS_BIT_STRING:
if (self->value.bitString.size == update->value.bitString.size)
memcpy(self->value.bitString.buf, update->value.bitString.buf, bitStringByteSize(self));
else
return false;
break;
case MMS_OCTET_STRING:
{
int size = update->value.octetString.size;
if (size > self->value.octetString.maxSize) {
GLOBAL_FREEMEM(self->value.octetString.buf);
self->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(size);
self->value.octetString.maxSize = size;
}
size = self->value.octetString.maxSize;
memcpy(self->value.octetString.buf, update->value.octetString.buf, size);
self->value.octetString.size = size;
}
break;
case MMS_VISIBLE_STRING:
MmsValue_setVisibleString(self, update->value.visibleString.buf);
break;
case MMS_STRING:
MmsValue_setMmsString(self, update->value.visibleString.buf);
break;
case MMS_BINARY_TIME:
self->value.binaryTime.size = update->value.binaryTime.size;
memcpy(self->value.binaryTime.buf, update->value.binaryTime.buf,
update->value.binaryTime.size);
break;
default:
return false;
break;
}
return true;
}
else
return false;
}
MmsValue*
@ -1015,9 +1024,7 @@ MmsValue_cloneToBuffer(const MmsValue* self, uint8_t* destinationAddress)
int i;
for (i = 0; i < self->value.structure.size; i++) {
newValue->value.structure.components[i] = (MmsValue*) destinationAddress;
//memcpy(&(newValue->value.structure.components[i]), &(destinationAddress), sizeof (MmsValue*));
memcpy(&(newValue->value.structure.components[i]), &(destinationAddress), sizeof (MmsValue*));
destinationAddress = MmsValue_cloneToBuffer(self->value.structure.components[i], destinationAddress);
}
}

@ -1,7 +1,7 @@
/*
* iso_server.c
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -51,6 +51,11 @@
struct sIsoServer {
IsoServerState state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore stateLock;
#endif
ConnectionIndicationHandler connectionHandler;
void* connectionHandlerParameter;
@ -83,6 +88,34 @@ struct sIsoServer {
int connectionCounter;
};
static void
setState(IsoServer self, IsoServerState newState)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->stateLock);
#endif
self->state = newState;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->stateLock);
#endif
}
static IsoServerState
getState(IsoServer self)
{
IsoServerState state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->stateLock);
#endif
state = self->state;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->stateLock);
#endif
return state;
}
#if (CONFIG_MMS_THREADLESS_STACK != 1) && (CONFIG_MMS_SINGLE_THREADED == 0)
static inline void
lockClientConnections(IsoServer self)
@ -301,7 +334,7 @@ setupIsoServer(IsoServer self)
self->serverSocket = (Socket) TcpServerSocket_create(self->localIpAddress, self->tcpPort);
if (self->serverSocket == NULL) {
self->state = ISO_SVR_STATE_ERROR;
setState(self, ISO_SVR_STATE_ERROR);
success = false;
goto exit_function;
@ -311,7 +344,7 @@ setupIsoServer(IsoServer self)
ServerSocket_listen((ServerSocket) self->serverSocket);
self->state = ISO_SVR_STATE_RUNNING;
setState(self, ISO_SVR_STATE_RUNNING);
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS != -1)
if (DEBUG_ISO_SERVER)
@ -438,6 +471,10 @@ IsoServer_create()
self->state = ISO_SVR_STATE_IDLE;
self->tcpPort = TCP_PORT;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->stateLock = Semaphore_create(1);
#endif
#if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1)
self->openClientConnections = LinkedList_create();
#else
@ -470,7 +507,7 @@ IsoServer_setLocalIpAddress(IsoServer self, char* ipAddress)
IsoServerState
IsoServer_getState(IsoServer self)
{
return self->state;
return getState(self);
}
void
@ -531,7 +568,7 @@ IsoServer_startListeningThreadless(IsoServer self)
self->serverSocket = NULL;
}
else {
self->state = ISO_SVR_STATE_RUNNING;
setState(self, ISO_SVR_STATE_RUNNING);
if (DEBUG_ISO_SERVER)
printf("ISO_SERVER: new iso server (threadless) started\n");
@ -543,7 +580,7 @@ IsoServer_waitReady(IsoServer self, unsigned int timeoutMs)
{
int result;
if (self->state == ISO_SVR_STATE_RUNNING) {
if (getState(self) == ISO_SVR_STATE_RUNNING) {
HandleSet handles;
handles = Handleset_new();
@ -613,14 +650,15 @@ IsoServer_waitReady(IsoServer self, unsigned int timeoutMs)
void
IsoServer_processIncomingMessages(IsoServer self)
{
if (self->state == ISO_SVR_STATE_RUNNING)
if (getState(self) == ISO_SVR_STATE_RUNNING)
handleIsoConnectionsThreadless(self);
}
static void
stopListening(IsoServer self)
{
self->state = ISO_SVR_STATE_STOPPED;
setState(self, ISO_SVR_STATE_STOPPED);
if (self->serverSocket != NULL) {
ServerSocket_destroy((ServerSocket) self->serverSocket);
self->serverSocket = NULL;
@ -661,7 +699,7 @@ IsoServer_stopListening(IsoServer self)
void
IsoServer_closeConnection(IsoServer self, IsoConnection isoConnection)
{
if (self->state != ISO_SVR_STATE_IDLE) {
if (getState(self) != ISO_SVR_STATE_IDLE) {
self->connectionHandler(ISO_CONNECTION_CLOSED, self->connectionHandlerParameter,
isoConnection);
}
@ -709,6 +747,10 @@ IsoServer_destroy(IsoServer self)
Semaphore_destroy(self->openClientConnectionsMutex);
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->stateLock);
#endif
GLOBAL_FREEMEM(self);
}

@ -398,8 +398,10 @@ SV_ASDU_encodeToBuffer(SV_ASDU self, uint8_t* buffer, int bufPos)
buffer[bufPos++] = self->smpSynch;
/* SmpRate */
bufPos = BerEncoder_encodeTL(0x86, 2, buffer, bufPos);
bufPos = encodeUInt16FixedSize(self->smpRate, buffer, bufPos);
if (self->hasSmpRate) {
bufPos = BerEncoder_encodeTL(0x86, 2, buffer, bufPos);
bufPos = encodeUInt16FixedSize(self->smpRate, buffer, bufPos);
}
/* Sample */
bufPos = BerEncoder_encodeTL(0x87, self->dataSize, buffer, bufPos);

Loading…
Cancel
Save