Merge branch 'master' of mz-automation.de:libiec61850

pull/6/head
Michael Zillgith 11 years ago
commit 1e018287b2

@ -113,6 +113,9 @@ namespace IEC61850
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getServerDirectory (IntPtr self, out int error, bool getFileNames); static extern IntPtr IedConnection_getServerDirectory (IntPtr self, out int error, bool getFileNames);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_getDeviceModelFromServer(IntPtr self, out int error);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getLogicalDeviceDirectory (IntPtr self, out int error, string logicalDeviceName); static extern IntPtr IedConnection_getLogicalDeviceDirectory (IntPtr self, out int error, string logicalDeviceName);
@ -255,6 +258,16 @@ namespace IEC61850
return controlObject; return controlObject;
} }
public void UpdateDeviceModel()
{
int error;
IedConnection_getDeviceModelFromServer(connection, out error);
if (error != 0)
throw new IedConnectionException("UpdateDeviceModel failed", error);
}
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception> /// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public List<string> GetServerDirectory (bool fileDirectory = false) public List<string> GetServerDirectory (bool fileDirectory = false)
{ {

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

@ -85,10 +85,10 @@ editSgConfirmedHandler(void* parameter, SettingGroupControlBlock* sgcb,
{ {
printf("Received edit sg confirm for sg %i\n", editSg); printf("Received edit sg confirm for sg %i\n", editSg);
ptoc1Settings[editSg - 1].strVal = MmsValue_toFloat(IEDMODEL_PROT_PTOC1_StrVal_setMag_f->mmsValue); ptoc1Settings[editSg - 1].strVal = MmsValue_toFloat(IEDMODEL_SE_PROT_PTOC1_StrVal_setMag_f->mmsValue);
ptoc1Settings[editSg - 1].opDlTmms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_OpDlTmms_setVal->mmsValue); ptoc1Settings[editSg - 1].opDlTmms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_OpDlTmms_setVal->mmsValue);
ptoc1Settings[editSg - 1].rsDlTmms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_RsDlTmms_setVal->mmsValue); ptoc1Settings[editSg - 1].rsDlTmms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_RsDlTmms_setVal->mmsValue);
ptoc1Settings[editSg - 1].rstTms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_RstTms_setVal->mmsValue); ptoc1Settings[editSg - 1].rstTms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_RstTms_setVal->mmsValue);
if (IedServer_getActiveSettingGroup(iedServer, sgcb) == editSg) { if (IedServer_getActiveSettingGroup(iedServer, sgcb) == editSg) {
loadActiveSgValues(editSg); loadActiveSgValues(editSg);

@ -231,4 +231,3 @@ StringUtils_startsWith(char* string, char* prefix)
return false; return false;
} }

@ -54,15 +54,44 @@ typedef struct sSocket* Socket;
/** Opaque reference for a set of server and socket handles */ /** Opaque reference for a set of server and socket handles */
typedef struct sHandleSet* HandleSet; typedef struct sHandleSet* HandleSet;
/**
* \brief Create a new connection handle set (HandleSet)
*
* \return new HandleSet instance
*/
HandleSet HandleSet
Handleset_new(void); Handleset_new(void);
/**
* \brief add a soecket to an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
*/
void void
Handleset_addSocket(HandleSet self, const Socket sock); Handleset_addSocket(HandleSet self, const Socket sock);
/**
* \brief wait for a socket to become ready
*
* This function is corresponding to the BSD socket select function.
* It returns the number of sockets on which data is pending or 0 if no data is pending
* on any of the monitored connections. The function will return after "timeout" ms if no
* data is pending.
* The function shall return -1 if a socket error occures.
*
* \param self the HandleSet instance
* \param timeout in milliseconds (ms)
*/
int int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs); Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
/**
* \brief destroy the HandleSet instance
*
* \param self the HandleSet instance to destroy
*/
void void
Handleset_destroy(HandleSet self); Handleset_destroy(HandleSet self);

@ -89,7 +89,7 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
{ {
int result; int result;
if (self != NULL && self->maxHandle >= 0) { if ((self != NULL) && (self->maxHandle >= 0)) {
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = timeoutMs / 1000; timeout.tv_sec = timeoutMs / 1000;

@ -56,7 +56,7 @@ struct sServerSocket {
struct sHandleSet { struct sHandleSet {
fd_set handles; fd_set handles;
int maxHandle; SOCKET maxHandle;
}; };
HandleSet HandleSet
@ -66,7 +66,7 @@ Handleset_new(void)
if (result != NULL) { if (result != NULL) {
FD_ZERO(&result->handles); FD_ZERO(&result->handles);
result->maxHandle = -1; result->maxHandle = INVALID_SOCKET;
} }
return result; return result;
} }
@ -74,13 +74,13 @@ Handleset_new(void)
void void
Handleset_addSocket(HandleSet self, const Socket sock) Handleset_addSocket(HandleSet self, const Socket sock)
{ {
if (self != NULL && sock != NULL && sock->fd != -1) { if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) {
FD_SET(sock->fd, &self->handles); FD_SET(sock->fd, &self->handles);
if (sock->fd > self->maxHandle) {
if ((sock->fd > self->maxHandle) || (self->maxHandle == INVALID_SOCKET))
self->maxHandle = sock->fd; self->maxHandle = sock->fd;
} }
} }
}
int int
Handleset_waitReady(HandleSet self, unsigned int timeoutMs) Handleset_waitReady(HandleSet self, unsigned int timeoutMs)
@ -302,16 +302,6 @@ Socket_connect(Socket self, const char* address, int port)
FD_ZERO(&fdSet); FD_ZERO(&fdSet);
FD_SET(self->fd, &fdSet); FD_SET(self->fd, &fdSet);
// if (connect(self->fd, (struct sockaddr *) &serverAddress,sizeof(serverAddress)) < 0) {
// if (DEBUG_SOCKET)
// printf("WIN32_SOCKET: Socket failed connecting!\n");
// return false;
// }
// else {
//
// return true;
// }
if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK) if (WSAGetLastError() != WSAEWOULDBLOCK)
return false; return false;
@ -321,7 +311,7 @@ Socket_connect(Socket self, const char* address, int port)
timeout.tv_sec = self->connectTimeout / 1000; timeout.tv_sec = self->connectTimeout / 1000;
timeout.tv_usec = (self->connectTimeout % 1000) * 1000; timeout.tv_usec = (self->connectTimeout % 1000) * 1000;
if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) == SOCKET_ERROR) if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) < 0)
return false; return false;
else else
return true; return true;

@ -237,7 +237,7 @@ ClientGooseControlBlock_setDstAddress_vid(ClientGooseControlBlock self, uint16_t
self->dstAddress = newEmptyPhyCommAddress(); self->dstAddress = newEmptyPhyCommAddress();
MmsValue* vid = MmsValue_getElement(self->dstAddress, 2); MmsValue* vid = MmsValue_getElement(self->dstAddress, 2);
MmsValue_setUint8(vid, vidValue); MmsValue_setUint16(vid, vidValue);
} }
uint16_t uint16_t
@ -256,7 +256,7 @@ ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16
self->dstAddress = newEmptyPhyCommAddress(); self->dstAddress = newEmptyPhyCommAddress();
MmsValue* appid = MmsValue_getElement(self->dstAddress, 3); MmsValue* appid = MmsValue_getElement(self->dstAddress, 3);
MmsValue_setUint8(appid, appidValue); MmsValue_setUint16(appid, appidValue);
} }
static void static void

@ -328,6 +328,8 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value)
char* rptId = report->rptId; char* rptId = report->rptId;
printf("Report ID is null!\n");
if (rptId == NULL) if (rptId == NULL)
rptId = report->rcbReference; rptId = report->rcbReference;

@ -195,46 +195,69 @@ ClientDataSet_getDataSetSize(ClientDataSet self)
return 0; return 0;
} }
static bool bool
doesControlObjectMatch(char* objRef, char* cntrlObj) private_IedConnection_doesControlObjectMatch(char* objRef, char* cntrlObj)
{ {
int objRefLen = strlen(objRef); int i = 0;
while (objRef[i] != '/') {
if (objRef[i] != cntrlObj[i])
return false;
char* separator = strchr(cntrlObj, '$'); i++;
}
if (separator == NULL) if (cntrlObj[i] != '/')
return false; return false;
int sepLen = separator - cntrlObj; // --> LD is equal
i++;
if (sepLen >= objRefLen) while (objRef[i] != '.') {
if (objRef[i] != cntrlObj[i])
return false; return false;
i++;
}
if (memcmp(objRef, cntrlObj, sepLen) != 0) int j = i;
if (cntrlObj[j++] != '$')
return false; return false;
char* cntrlObjName = objRef + sepLen + 1; // --> LN is equal
if (separator[1] != 'C') if (cntrlObj[j++] != 'C')
return false; return false;
if (separator[2] != 'O') if (cntrlObj[j++] != 'O')
return false; return false;
if (separator[3] != '$') if (cntrlObj[j++] != '$')
return false; return false;
char* nextSeparator = strchr(separator + 4, '$'); // --> FC is ok
if (nextSeparator == NULL) i++;
return false;
int cntrlObjNameLen = strlen(cntrlObjName); while (objRef[i] != 0) {
if (cntrlObj[j] == 0)
return false;
if (cntrlObjNameLen != nextSeparator - (separator + 4)) if (objRef[i] == '.') {
if (cntrlObj[j] != '$')
return false;
}
else {
if (objRef[i] != cntrlObj[j])
return false; return false;
}
if (memcmp(cntrlObjName, separator + 4, cntrlObjNameLen) == 0) i++;
return true; j++;
}
if ((cntrlObj[j] == 0) || (cntrlObj[j] == '$'))
return true;
else
return false; return false;
} }
@ -357,7 +380,7 @@ handleLastApplErrorMessage(IedConnection self, MmsValue* lastApplError)
char* objectRef = ControlObjectClient_getObjectReference(object); char* objectRef = ControlObjectClient_getObjectReference(object);
if (doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) { if (private_IedConnection_doesControlObjectMatch(objectRef, MmsValue_toString(cntrlObj))) {
ControlObjectClient_setLastApplError(object, self->lastApplError); ControlObjectClient_setLastApplError(object, self->lastApplError);
} }
@ -966,10 +989,9 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error
LinkedList_destroy(logicalDeviceNames); LinkedList_destroy(logicalDeviceNames);
} }
else { else
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
} }
}
LinkedList /*<char*>*/ LinkedList /*<char*>*/
IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error) IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error)

@ -607,13 +607,16 @@ typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report);
/** /**
* \brief Install a report handler function for the specified report control block (RCB) * \brief Install a report handler function for the specified report control block (RCB)
* *
* It is important that you provide a ClientDataSet instance that is already populated with an MmsValue object * This function will replace a report handler set earlier for the specified RCB. The report handler
* of type MMS_STRUCTURE that contains the data set entries as structure elements. This is required because otherwise * will be called whenever a report for the specified RCB is received.
* the report handler is not able to correctly parse the report message from the server. * Please note that this function should be called whenever the RCB data set is changed or updated.
* Otherwise the internal data structures storing the received data set values will not be updated
* correctly.
* *
* This function will replace a formerly set report handler function for the specified RCB. * When replacing a report handler you only have to call this function. There is no separate call to
* IedConnection_uninstallReportHandler() required.
* *
* \param connection the connection object * \param self the connection object
* \param rcbReference object reference of the report control block * \param rcbReference object reference of the report control block
* \param rptId a string that identifies the report. If the rptId is not available then the * \param rptId a string that identifies the report. If the rptId is not available then the
* rcbReference is used to identify the report. * rcbReference is used to identify the report.
@ -626,6 +629,9 @@ IedConnection_installReportHandler(IedConnection self, char* rcbReference, char*
/** /**
* \brief uninstall a report handler function for the specified report control block (RCB) * \brief uninstall a report handler function for the specified report control block (RCB)
*
* \param self the connection object
* \param rcbReference object reference of the report control block
*/ */
void void
IedConnection_uninstallReportHandler(IedConnection self, char* rcbReference); IedConnection_uninstallReportHandler(IedConnection self, char* rcbReference);

@ -253,36 +253,52 @@ struct sGSEControlBlock {
/** /**
* \brief get the number of direct children of a model node * \brief get the number of direct children of a model node
* *
* \param node the model node instance * \param self the model node instance
* *
* \return the number of children of the model node * \return the number of children of the model node
* ¸ * ¸
*/ */
int int
ModelNode_getChildCount(ModelNode* modelNode); ModelNode_getChildCount(ModelNode* self);
/** /**
* \brief return a child model node * \brief return a child model node
* *
* \param node the model node instance * \param self the model node instance
* \param the name of the child model node * \param name the name of the child model node
* *
* \return the model node instance or NULL if model node does not exist. * \return the model node instance or NULL if model node does not exist.
*/ */
ModelNode* ModelNode*
ModelNode_getChild(ModelNode* modelNode, const char* name); ModelNode_getChild(ModelNode* self, const char* name);
/**
* \brief return a child model node with a given functional constraint
*
* Sometimes the name is not enough to identify a model node. This is the case when
* editable setting groups are used. In this case the setting group members have two different
* model nodes associated that differ in their FC (SG and SE).
*
* \param self the model node instance
* \param name the name of the child model node
* \param fc the functional constraint of the model node
*
* \return the model node instance or NULL if model node does not exist.
*/
ModelNode*
ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint fc);
/** /**
* \brief Return the IEC 61850 object reference of a model node * \brief Return the IEC 61850 object reference of a model node
* *
* \param node the model node instance * \param self the model node instance
* \param objectReference pointer to a buffer where to write the object reference string. If NULL * \param objectReference pointer to a buffer where to write the object reference string. If NULL
* is given the buffer is allocated by the function. * is given the buffer is allocated by the function.
* *
* \return the object reference string * \return the object reference string
*/ */
char* char*
ModelNode_getObjectReference(ModelNode* node, char* objectReference); ModelNode_getObjectReference(ModelNode* self, char* objectReference);
/** /**
* \brief Set the name of the IED * \brief Set the name of the IED

@ -71,6 +71,9 @@ struct sClientReportControlBlock {
IedClientError IedClientError
private_IedConnection_mapMmsErrorToIedError(MmsError mmsError); private_IedConnection_mapMmsErrorToIedError(MmsError mmsError);
bool
private_IedConnection_doesControlObjectMatch(char* objRef, char* cntrlObj);
void void
private_IedConnection_addControlClient(IedConnection self, ControlObjectClient control); private_IedConnection_addControlClient(IedConnection self, ControlObjectClient control);

@ -1602,6 +1602,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
if ((val > 0) && (val <= sg->sgcb->numOfSGs)) { if ((val > 0) && (val <= sg->sgcb->numOfSGs)) {
if (sg->editSgChangedHandler != NULL) { if (sg->editSgChangedHandler != NULL) {
if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb, if (sg->editSgChangedHandler(sg->editSgChangedHandlerParameter, sg->sgcb,
(uint8_t) val, (ClientConnection) connection)) (uint8_t) val, (ClientConnection) connection))
{ {

@ -328,7 +328,6 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) if (self->inclusionFlags[i] != REPORT_CONTROL_NONE)
addReferenceForEntry = true; addReferenceForEntry = true;
if (addReferenceForEntry) { if (addReferenceForEntry) {
char dataReference[130]; char dataReference[130];

@ -58,7 +58,6 @@ readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize)
} }
} }
if (fileReadResult > 0) { if (fileReadResult > 0) {
while (fileReadResult > 0) { while (fileReadResult > 0) {
fileReadResult = FileSystem_readFile(fileHandle, buffer + bufPos, 1); fileReadResult = FileSystem_readFile(fileHandle, buffer + bufPos, 1);
@ -238,8 +237,12 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
#if (CONFIG_IEC61850_SETTING_GROUPS == 1) #if (CONFIG_IEC61850_SETTING_GROUPS == 1)
else if (StringUtils_startsWith((char*) lineBuffer, "SG")) { else if (StringUtils_startsWith((char*) lineBuffer, "SG")) {
if (strcmp(currentLN->name, "LLN0") != 0) if (strcmp(currentLN->name, "LLN0") != 0) {
if (DEBUG_IED_SERVER)
printf("Setting group control is not defined in LLN0\n");
goto exit_error; goto exit_error;
}
int actSG; int actSG;
int numOfSGs; int numOfSGs;
@ -253,8 +256,12 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
} }
#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
else else {
// if (DEBUG_IED_SERVER)
printf("IED_SERVER: Unknown identifier (%s)\n", lineBuffer);
goto exit_error; goto exit_error;
}
} }
else if (indendation > 3) { else if (indendation > 3) {
@ -423,8 +430,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
return model; return model;
exit_error: exit_error:
if (DEBUG_IED_SERVER) // if (DEBUG_IED_SERVER)
printf("error parsing line %i\n", currentLine); printf("IED_SERVER: error parsing line %i (indendation level = %i)\n", currentLine, indendation);
IedModel_destroy(model); IedModel_destroy(model);
return NULL; return NULL;
} }

@ -457,6 +457,56 @@ ModelNode_getChild(ModelNode* self, const char* name)
return matchingNode; return matchingNode;
} }
ModelNode*
ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint fc)
{
// check for separator
const char* separator = strchr(name, '.');
int nameElementLength = 0;
if (separator != NULL)
nameElementLength = (separator - name);
else
nameElementLength = strlen(name);
ModelNode* nextNode = self->firstChild;
ModelNode* matchingNode = NULL;
while (nextNode != NULL) {
int nodeNameLen = strlen(nextNode->name);
if (nodeNameLen == nameElementLength) {
if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
if (separator == NULL) {
if (nextNode->modelType == DataAttributeModelType) {
DataAttribute* da = (DataAttribute*) nextNode;
if (da->fc == fc) {
matchingNode = nextNode;
break;
}
}
}
else {
matchingNode = nextNode;
break;
}
}
}
nextNode = nextNode->sibling;
}
if ((separator != NULL) && (matchingNode != NULL)) {
return ModelNode_getChildWithFc(matchingNode, separator + 1, fc);
}
else
return matchingNode;
}
inline inline
LogicalNode* LogicalNode*

@ -338,10 +338,7 @@ IsoSession_createFinishSpdu(IsoSession* self, BufferChain buffer, BufferChain pa
buf[offset++] = 9; /* FINISH-SPDU code */ buf[offset++] = 9; /* FINISH-SPDU code */
buf[offset++] = 5 + payload->length; /* LI */ buf[offset++] = 2 + payload->length; /* LI */
buf[offset++] = 17; /* PI-Code transport-disconnect */
buf[offset++] = 1; /* LI = 1 */
buf[offset++] = 2; /* transport-connection-released */
buf[offset++] = 193; /* PGI-Code user data */ buf[offset++] = 193; /* PGI-Code user data */
buf[offset++] = payload->length; /* LI of user data */ buf[offset++] = payload->length; /* LI of user data */

@ -481,4 +481,5 @@ EXPORTS
IedServer_getUTCTimeAttributeValue IedServer_getUTCTimeAttributeValue
IedServer_getBitStringAttributeValue IedServer_getBitStringAttributeValue
IedServer_getStringAttributeValue IedServer_getStringAttributeValue
ModelNode_getChildWithFc

@ -505,4 +505,5 @@ EXPORTS
IedServer_getUTCTimeAttributeValue IedServer_getUTCTimeAttributeValue
IedServer_getBitStringAttributeValue IedServer_getBitStringAttributeValue
IedServer_getStringAttributeValue IedServer_getStringAttributeValue
ModelNode_getChildWithFc

Loading…
Cancel
Save