- fixed problem with SqNum (INT8U/INT16U) in RCBs and reports
- started to implement sorting algorithm for getNameList
This commit is contained in:
parent
3afba2c958
commit
947b4a0cd5
7 changed files with 391 additions and 13 deletions
|
@ -182,10 +182,10 @@
|
|||
#define CONFIG_IEC61850_CONTROL_SERVICE 0
|
||||
#endif
|
||||
|
||||
/* support flatted named variable name space required by IEC 61850-8-1 MMS mapping */
|
||||
/* support flat named variable name space required by IEC 61850-8-1 MMS mapping */
|
||||
#define CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE 1
|
||||
|
||||
/* VMD scope named variables are not used by IEC 61850 */
|
||||
/* VMD scope named variables are not used by IEC 61850 (one application is ICCP) */
|
||||
#define CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES 0
|
||||
|
||||
#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#define STRING_UTILITIES_H_
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
#include "linked_list.h"
|
||||
|
||||
char*
|
||||
copyString(const char* string);
|
||||
|
@ -76,4 +77,34 @@ StringUtils_createBufferFromHexString(char* hexString, uint8_t* buffer);
|
|||
bool
|
||||
StringUtils_startsWith(char* string, char* prefix);
|
||||
|
||||
/**
|
||||
* \brief Compare to characters using the collation order as defined in ISO 9506-2 7.5.2
|
||||
*
|
||||
* \param a the first string
|
||||
* \param b the second string
|
||||
*
|
||||
* \returns 0 if a equals b; a positive number if b > a; a negative number if b < a
|
||||
*/
|
||||
int
|
||||
StringUtils_compareChars(char a, char b);
|
||||
|
||||
/**
|
||||
* \brief Compare to strings using the collation order as defined in ISO 9506-2 7.5.2
|
||||
*
|
||||
* \param a the first string
|
||||
* \param b the second string
|
||||
*
|
||||
* \returns 0 if a equals b; a positive number if b > a; a negative number if b < a
|
||||
*/
|
||||
int
|
||||
StringUtils_compareStrings(const char* a, const char* b);
|
||||
|
||||
/**
|
||||
* \brief sort a list of strings alphabetically (according to the MMS identifier collation order)
|
||||
*
|
||||
* \param list a list that contains string elements
|
||||
*/
|
||||
void
|
||||
StringUtils_sortList(LinkedList list);
|
||||
|
||||
#endif /* STRING_UTILITIES_H_ */
|
||||
|
|
|
@ -240,3 +240,129 @@ StringUtils_startsWith(char* string, char* prefix)
|
|||
return false;
|
||||
}
|
||||
|
||||
#define LT_MAX_CHARS 128
|
||||
|
||||
static int
|
||||
getCharWeight(int c)
|
||||
{
|
||||
static bool initialized = false;
|
||||
static char lookupTable[LT_MAX_CHARS + 1];
|
||||
static char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789";
|
||||
|
||||
if (!initialized) {
|
||||
int ltIndex;
|
||||
int weight = 1;
|
||||
|
||||
for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) {
|
||||
if (strchr(charOrder, ltIndex)) continue;
|
||||
lookupTable[ltIndex] = weight;
|
||||
weight++;
|
||||
}
|
||||
|
||||
int charIndex;
|
||||
|
||||
for (charIndex = 0; charOrder[charIndex]; charIndex++) {
|
||||
lookupTable[(int)charOrder[charIndex]] = weight;
|
||||
weight++;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
if ((c < 1) || (c > LT_MAX_CHARS))
|
||||
return c;
|
||||
else
|
||||
return lookupTable[c];
|
||||
}
|
||||
|
||||
int
|
||||
StringUtils_compareChars(char a, char b)
|
||||
{
|
||||
return (getCharWeight(a) - getCharWeight(b));
|
||||
}
|
||||
|
||||
int
|
||||
StringUtils_compareStrings(const char* a, const char* b)
|
||||
{
|
||||
int diff = StringUtils_compareChars(*a, *b);
|
||||
|
||||
while (diff == 0) {
|
||||
if ((*a == 0) || (*b == 0)) {
|
||||
return b - a;
|
||||
}
|
||||
|
||||
diff = StringUtils_compareChars(*++a, *++b);
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
void
|
||||
StringUtils_sortList(LinkedList list)
|
||||
{
|
||||
LinkedList firstElement = list->next;
|
||||
LinkedList prevElement = NULL;
|
||||
|
||||
while (true) {
|
||||
|
||||
/* Check for end of list */
|
||||
if (firstElement->next) {
|
||||
|
||||
char* str1 = (char*) LinkedList_getData(firstElement);
|
||||
char* str2 = (char*) LinkedList_getData(firstElement->next);
|
||||
|
||||
/* Compare first element with next element */
|
||||
if (StringUtils_compareStrings(str1, str2) > 0) {
|
||||
LinkedList removedElement = firstElement;
|
||||
|
||||
if (firstElement == list->next)
|
||||
list->next = firstElement->next;
|
||||
else
|
||||
prevElement->next = firstElement->next;
|
||||
|
||||
firstElement = firstElement->next;
|
||||
|
||||
/* search for place to insert */
|
||||
LinkedList compareElement = removedElement->next->next;
|
||||
LinkedList prevCompareElement = removedElement->next;
|
||||
|
||||
while (true) {
|
||||
str2 = (char*) LinkedList_getData(compareElement);
|
||||
|
||||
if (StringUtils_compareStrings(str1, str2) < 0) {
|
||||
/* insert element before */
|
||||
prevCompareElement->next = removedElement;
|
||||
removedElement->next = compareElement;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
prevCompareElement = compareElement;
|
||||
compareElement = compareElement->next;
|
||||
|
||||
/* Are we at the end of the list? */
|
||||
if (compareElement == NULL) {
|
||||
/* Insert removed element at end of list */
|
||||
prevCompareElement->next = removedElement;
|
||||
removedElement->next = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
prevElement = firstElement;
|
||||
firstElement = firstElement->next;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -501,7 +501,6 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain,
|
|||
"$GO$", gooseControlBlock->name);
|
||||
|
||||
if (gooseControlBlock->appId != NULL) {
|
||||
// gcb->goID = copyString(gooseControlBlock->appId);
|
||||
MmsValue* goID = MmsValue_getElement(gseValues, 1);
|
||||
|
||||
MmsValue_setVisibleString(goID, gooseControlBlock->appId);
|
||||
|
|
|
@ -432,7 +432,7 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI)
|
|||
if (self->sqNum == 256)
|
||||
self->sqNum = 0;
|
||||
|
||||
MmsValue_setUint8(sqNum, self->sqNum);
|
||||
MmsValue_setUint16(sqNum, self->sqNum);
|
||||
|
||||
LinkedList_destroyDeep(deletableElements, (LinkedListValueDeleteFunction) MmsValue_delete);
|
||||
LinkedList_destroyStatic(reportElements);
|
||||
|
@ -1225,7 +1225,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
|
|||
|
||||
MmsValue* sqNum = ReportControl_getRCBValue(rc, "SqNum");
|
||||
|
||||
MmsValue_setUint16(sqNum, 0U);
|
||||
MmsValue_setUint32(sqNum, 0U);
|
||||
|
||||
retVal = DATA_ACCESS_ERROR_SUCCESS;
|
||||
goto exit_function;
|
||||
|
@ -2165,7 +2165,7 @@ sendNextReportEntry(ReportControl* self)
|
|||
|
||||
/* Increase sequence number */
|
||||
self->sqNum++;
|
||||
MmsValue_setUint16(sqNum, self->sqNum);
|
||||
MmsValue_setUint32(sqNum, self->sqNum);
|
||||
|
||||
assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* mms_get_namelist_service.c
|
||||
*
|
||||
* Copyright 2013, 2014 Michael Zillgith
|
||||
* Copyright 2013, 2014, 2015 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
|
@ -125,10 +125,12 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec
|
|||
|
||||
return listElement;
|
||||
}
|
||||
|
||||
#endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */
|
||||
|
||||
static LinkedList
|
||||
getNameListDomainSpecific(MmsServerConnection connection, char* domainName) {
|
||||
getNameListDomainSpecific(MmsServerConnection connection, char* domainName)
|
||||
{
|
||||
MmsDevice* device = MmsServer_getDevice(connection->server);
|
||||
|
||||
LinkedList nameList = NULL;
|
||||
|
@ -157,6 +159,85 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) {
|
|||
return nameList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
typedef struct sGetNextListElementDomainSpecificState {
|
||||
const char* continueAfter;
|
||||
MmsVariableSpecification** variables;
|
||||
int variablesCount;
|
||||
} GetNextListElementDomainSpecificState;
|
||||
|
||||
|
||||
static bool
|
||||
getNextListElement_DomainSpecific_init(MmsServerConnection connection, char* domainName,
|
||||
GetNextListElementDomainSpecificState* state, const char* continueAfter)
|
||||
{
|
||||
MmsDevice* device = MmsServer_getDevice(connection->server);
|
||||
MmsDomain* domain = MmsDevice_getDomain(device, domainName);
|
||||
|
||||
state->continueAfter = continueAfter;
|
||||
|
||||
if (domain != NULL) {
|
||||
state->variables = domain->namedVariables;
|
||||
state->variablesCount = domain->namedVariablesCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static LinkedList
|
||||
TEMP_addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpecification* variable)
|
||||
{
|
||||
LinkedList listElement = nameList;
|
||||
|
||||
if (variable->type == MMS_STRUCTURE) {
|
||||
|
||||
int i;
|
||||
|
||||
MmsVariableSpecification** variables = variable->typeSpec.structure.elements;
|
||||
|
||||
for (i = 0; i < variable->typeSpec.structure.elementCount; i++) {
|
||||
char* variableName = appendMmsSubVariable(prefix, variables[i]->name);
|
||||
|
||||
listElement = LinkedList_insertAfter(listElement, variableName);
|
||||
|
||||
listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return listElement;
|
||||
}
|
||||
|
||||
|
||||
// return NULL if no more elements are left
|
||||
static char*
|
||||
getNextListElement_DomainSpecific(void* stateParameter)
|
||||
{
|
||||
GetNextListElementDomainSpecificState* state = (GetNextListElementDomainSpecificState*) stateParameter;
|
||||
|
||||
= NULL;
|
||||
|
||||
int i;
|
||||
|
||||
LinkedList element = nameList;
|
||||
|
||||
for (i = 0; i < state->variablesCount; i++) {
|
||||
element = LinkedList_insertAfter(element, copyString(state->variables[i]->name));
|
||||
|
||||
#if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1)
|
||||
char* prefix = state->variables[i]->name;
|
||||
element = TEMP_addSubNamedVaribleNamesToList(element, prefix, state->variables[i]);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
return nameList;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if (MMS_DATA_SET_SERVICE == 1)
|
||||
|
||||
static LinkedList
|
||||
|
@ -325,9 +406,125 @@ createNameListResponse(
|
|||
response->size = bufPos;
|
||||
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("getNameList: encoded %i bytes\n", response->size);
|
||||
printf("MMS_SERVER: getNameList: encoded %i bytes\n", response->size);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
NEW_createNameListResponse(
|
||||
MmsServerConnection connection,
|
||||
int invokeId,
|
||||
LinkedList nameList,
|
||||
ByteBuffer* response,
|
||||
char* continueAfter)
|
||||
{
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
//TODO move continueAfter handling in variable name generation code!
|
||||
LinkedList startElement = NULL;
|
||||
|
||||
|
||||
if (continueAfter != NULL) {
|
||||
LinkedList element = nameList;
|
||||
|
||||
while ((element = LinkedList_getNext(element)) != NULL) {
|
||||
if (strcmp((char*) (element->data), continueAfter) == 0) {
|
||||
startElement = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startElement == NULL) {
|
||||
mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* determine number of identifiers to include in response */
|
||||
if (startElement == NULL)
|
||||
startElement = nameList;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int nameCount = 0;
|
||||
int estimatedMmsPduLength = 27; /* estimated overhead size of PDU encoding */
|
||||
int maxPduSize = connection->maxPduSize;
|
||||
|
||||
bool moreFollows = false;
|
||||
|
||||
LinkedList element = startElement;
|
||||
|
||||
uint32_t identifierListSize = 0;
|
||||
|
||||
while ((element = LinkedList_getNext(element)) != NULL) {
|
||||
int elementLength;
|
||||
|
||||
elementLength = BerEncoder_determineEncodedStringSize((char*) element->data);
|
||||
|
||||
if ((estimatedMmsPduLength + elementLength) > maxPduSize) {
|
||||
moreFollows = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
estimatedMmsPduLength += elementLength;
|
||||
identifierListSize += elementLength;
|
||||
nameCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint32_t listOfIdentifierSize = 1 + BerEncoder_determineLengthSize(identifierListSize) + identifierListSize;
|
||||
|
||||
uint32_t getNameListSize = listOfIdentifierSize;
|
||||
|
||||
if (moreFollows == false)
|
||||
getNameListSize += 3;
|
||||
|
||||
uint32_t confirmedServiceResponseSize = 1 + BerEncoder_determineLengthSize(getNameListSize) + getNameListSize;
|
||||
|
||||
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2;
|
||||
|
||||
uint32_t confirmedResponsePDUSize = confirmedServiceResponseSize + invokeIdSize;
|
||||
|
||||
/* encode response */
|
||||
element = startElement;
|
||||
|
||||
uint8_t* buffer = response->buffer;
|
||||
int bufPos = 0;
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeUInt32((uint32_t) invokeId, buffer, bufPos);
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0xa1, getNameListSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeTL(0xa0, identifierListSize, buffer, bufPos);
|
||||
|
||||
int i = 0;
|
||||
|
||||
while ((element = LinkedList_getNext(element)) != NULL) {
|
||||
bufPos = BerEncoder_encodeStringWithTag(0x1a, (char*) element->data, buffer, bufPos);
|
||||
|
||||
i++;
|
||||
|
||||
if (i == nameCount)
|
||||
break;
|
||||
}
|
||||
|
||||
if (moreFollows == false)
|
||||
bufPos = BerEncoder_encodeBoolean(0x81, moreFollows, buffer, bufPos);
|
||||
|
||||
response->size = bufPos;
|
||||
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: getNameList: encoded %i bytes\n", response->size);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
mmsServer_handleGetNameListRequest(
|
||||
MmsServerConnection connection,
|
||||
|
@ -352,7 +549,7 @@ mmsServer_handleGetNameListRequest(
|
|||
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
|
||||
|
||||
if (bufPos < 0) {
|
||||
//writeMmsRejectPdu(&invokeId, REJECT_UNRECOGNIZED_SERVICE, response);
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -410,7 +607,7 @@ mmsServer_handleGetNameListRequest(
|
|||
continueAfterId[continueAfterLength] = 0;
|
||||
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("continue after: (%s)\n", continueAfterId);
|
||||
printf("MMS_SERVER: getNameListRequest - continue after: (%s)\n", continueAfterId);
|
||||
}
|
||||
|
||||
if (objectScope == OBJECT_SCOPE_DOMAIN) {
|
||||
|
@ -421,13 +618,23 @@ mmsServer_handleGetNameListRequest(
|
|||
|
||||
if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) {
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("get namelist for (%s)\n", domainSpecificName);
|
||||
printf("MMS_SERVER: get namelist for (%s)\n", domainSpecificName);
|
||||
|
||||
LinkedList nameList = getNameListDomainSpecific(connection, domainSpecificName);
|
||||
|
||||
if (nameList == NULL)
|
||||
mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
|
||||
else {
|
||||
|
||||
printf("\n-------------\n");
|
||||
LinkedList_printStringList(nameList);
|
||||
|
||||
StringUtils_sortList(nameList);
|
||||
|
||||
printf("\n-------------\n");
|
||||
LinkedList_printStringList(nameList);
|
||||
printf("\n-------------\n");
|
||||
|
||||
createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
|
||||
LinkedList_destroy(nameList);
|
||||
}
|
||||
|
@ -439,6 +646,8 @@ mmsServer_handleGetNameListRequest(
|
|||
if (nameList == NULL)
|
||||
mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
|
||||
else {
|
||||
StringUtils_sortList(nameList);
|
||||
|
||||
createNameListResponse(connection, invokeId, nameList, response, continueAfter);
|
||||
LinkedList_destroy(nameList);
|
||||
}
|
||||
|
@ -458,6 +667,8 @@ mmsServer_handleGetNameListRequest(
|
|||
|
||||
LinkedList nameList = getDomainNames(connection);
|
||||
|
||||
StringUtils_sortList(nameList);
|
||||
|
||||
createNameListResponse(connection, invokeId, nameList, response, continueAfter);
|
||||
|
||||
LinkedList_destroyStatic(nameList);
|
||||
|
@ -477,6 +688,8 @@ mmsServer_handleGetNameListRequest(
|
|||
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) {
|
||||
LinkedList nameList = getnamedVariableListsVMDSpecific(connection);
|
||||
|
||||
StringUtils_sortList(nameList);
|
||||
|
||||
createNameListResponse(connection, invokeId, nameList, response, continueAfter);
|
||||
|
||||
LinkedList_destroy(nameList);
|
||||
|
@ -486,6 +699,8 @@ mmsServer_handleGetNameListRequest(
|
|||
else if (objectClass == OBJECT_CLASS_JOURNAL) {
|
||||
LinkedList nameList = LinkedList_create();
|
||||
|
||||
// StringUtils_sortList(nameList);
|
||||
|
||||
createNameListResponse(connection, invokeId, nameList, response, continueAfter);
|
||||
|
||||
LinkedList_destroy(nameList);
|
||||
|
@ -505,6 +720,8 @@ mmsServer_handleGetNameListRequest(
|
|||
if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) {
|
||||
LinkedList nameList = getNamedVariableListAssociationSpecific(connection);
|
||||
|
||||
StringUtils_sortList(nameList);
|
||||
|
||||
createNameListResponse(connection, invokeId, nameList, response, continueAfter);
|
||||
|
||||
LinkedList_destroy(nameList);
|
||||
|
@ -516,7 +733,7 @@ mmsServer_handleGetNameListRequest(
|
|||
#endif /* (MMS_DATA_SET_SERVICE == 1) */
|
||||
|
||||
else {
|
||||
if (DEBUG_MMS_SERVER) printf("mms_server: getNameList(%i) not supported!\n", objectScope);
|
||||
if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList(%i) not supported!\n", objectScope);
|
||||
mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,11 @@ mmsServer_writeMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response
|
|||
mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =
|
||||
RejectPDU__rejectReason__confirmedRequestPDU_invalidArgument;
|
||||
}
|
||||
else if (reason == MMS_ERROR_REJECT_INVALID_PDU) {
|
||||
mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_pduError;
|
||||
mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =
|
||||
RejectPDU__rejectReason__pduError_invalidPdu;
|
||||
}
|
||||
else {
|
||||
mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU;
|
||||
mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =
|
||||
|
|
Loading…
Add table
Reference in a new issue