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

This commit is contained in:
Michael Zillgith 2015-01-12 11:34:04 +01:00
commit 1e018287b2
20 changed files with 217 additions and 77 deletions

View file

@ -111,7 +111,10 @@ namespace IEC61850
static extern IntPtr IedConnection_getLogicalNodeDirectory (IntPtr self, out int error, string logicalNodeReference, int acsiClass);
[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)]
static extern IntPtr IedConnection_getLogicalDeviceDirectory (IntPtr self, out int error, string logicalDeviceName);
@ -253,7 +256,17 @@ namespace IEC61850
ControlObject controlObject = new ControlObject (objectReference, connection, this);
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>
public List<string> GetServerDirectory (bool fileDirectory = false)

View file

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

View file

@ -85,10 +85,10 @@ editSgConfirmedHandler(void* parameter, SettingGroupControlBlock* sgcb,
{
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].opDlTmms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_OpDlTmms_setVal->mmsValue);
ptoc1Settings[editSg - 1].rsDlTmms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_RsDlTmms_setVal->mmsValue);
ptoc1Settings[editSg - 1].rstTms = MmsValue_toInt32(IEDMODEL_PROT_PTOC1_RstTms_setVal->mmsValue);
ptoc1Settings[editSg - 1].strVal = MmsValue_toFloat(IEDMODEL_SE_PROT_PTOC1_StrVal_setMag_f->mmsValue);
ptoc1Settings[editSg - 1].opDlTmms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_OpDlTmms_setVal->mmsValue);
ptoc1Settings[editSg - 1].rsDlTmms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_RsDlTmms_setVal->mmsValue);
ptoc1Settings[editSg - 1].rstTms = MmsValue_toInt32(IEDMODEL_SE_PROT_PTOC1_RstTms_setVal->mmsValue);
if (IedServer_getActiveSettingGroup(iedServer, sgcb) == editSg) {
loadActiveSgValues(editSg);

View file

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

View file

@ -54,15 +54,44 @@ typedef struct sSocket* Socket;
/** Opaque reference for a set of server and socket handles */
typedef struct sHandleSet* HandleSet;
/**
* \brief Create a new connection handle set (HandleSet)
*
* \return new HandleSet instance
*/
HandleSet
Handleset_new(void);
/**
* \brief add a soecket to an existing handle set
*
* \param self the HandleSet instance
* \param sock the socket to add
*/
void
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
Handleset_waitReady(HandleSet self, unsigned int timeoutMs);
/**
* \brief destroy the HandleSet instance
*
* \param self the HandleSet instance to destroy
*/
void
Handleset_destroy(HandleSet self);

View file

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

View file

@ -56,7 +56,7 @@ struct sServerSocket {
struct sHandleSet {
fd_set handles;
int maxHandle;
SOCKET maxHandle;
};
HandleSet
@ -66,7 +66,7 @@ Handleset_new(void)
if (result != NULL) {
FD_ZERO(&result->handles);
result->maxHandle = -1;
result->maxHandle = INVALID_SOCKET;
}
return result;
}
@ -74,11 +74,11 @@ Handleset_new(void)
void
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);
if (sock->fd > self->maxHandle) {
if ((sock->fd > self->maxHandle) || (self->maxHandle == INVALID_SOCKET))
self->maxHandle = sock->fd;
}
}
}
@ -302,16 +302,6 @@ Socket_connect(Socket self, const char* address, int port)
FD_ZERO(&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 (WSAGetLastError() != WSAEWOULDBLOCK)
return false;
@ -321,7 +311,7 @@ Socket_connect(Socket self, const char* address, int port)
timeout.tv_sec = self->connectTimeout / 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;
else
return true;

View file

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

View file

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

View file

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

View file

@ -607,13 +607,16 @@ typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report);
/**
* \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
* of type MMS_STRUCTURE that contains the data set entries as structure elements. This is required because otherwise
* the report handler is not able to correctly parse the report message from the server.
* This function will replace a report handler set earlier for the specified RCB. The report handler
* will be called whenever a report for the specified RCB is received.
* 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 rptId a string that identifies the report. If the rptId is not available then the
* 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)
*
* \param self the connection object
* \param rcbReference object reference of the report control block
*/
void
IedConnection_uninstallReportHandler(IedConnection self, char* rcbReference);

View file

@ -253,36 +253,52 @@ struct sGSEControlBlock {
/**
* \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
* ¸
*/
int
ModelNode_getChildCount(ModelNode* modelNode);
ModelNode_getChildCount(ModelNode* self);
/**
* \brief return a child model node
*
* \param node the model node instance
* \param the name of the child model node
* \param self the model node instance
* \param name the name of the child model node
*
* \return the model node instance or NULL if model node does not exist.
*/
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
*
* \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
* is given the buffer is allocated by the function.
*
* \return the object reference string
*/
char*
ModelNode_getObjectReference(ModelNode* node, char* objectReference);
ModelNode_getObjectReference(ModelNode* self, char* objectReference);
/**
* \brief Set the name of the IED

View file

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

View file

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

View file

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

View file

@ -58,7 +58,6 @@ readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize)
}
}
if (fileReadResult > 0) {
while (fileReadResult > 0) {
fileReadResult = FileSystem_readFile(fileHandle, buffer + bufPos, 1);
@ -238,8 +237,12 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
#if (CONFIG_IEC61850_SETTING_GROUPS == 1)
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;
}
int actSG;
int numOfSGs;
@ -253,8 +256,12 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
}
#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */
else
else {
// if (DEBUG_IED_SERVER)
printf("IED_SERVER: Unknown identifier (%s)\n", lineBuffer);
goto exit_error;
}
}
else if (indendation > 3) {
@ -423,8 +430,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle)
return model;
exit_error:
if (DEBUG_IED_SERVER)
printf("error parsing line %i\n", currentLine);
// if (DEBUG_IED_SERVER)
printf("IED_SERVER: error parsing line %i (indendation level = %i)\n", currentLine, indendation);
IedModel_destroy(model);
return NULL;
}

View file

@ -457,6 +457,56 @@ ModelNode_getChild(ModelNode* self, const char* name)
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
LogicalNode*

View file

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

View file

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

View file

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