- added IEC 61850 client API to query logs
- added client example to read logs
This commit is contained in:
parent
922c5eec50
commit
97fd7524f8
10 changed files with 329 additions and 68 deletions
|
@ -18,6 +18,7 @@ add_subdirectory(iec61850_client_example4)
|
|||
add_subdirectory(iec61850_client_example5)
|
||||
add_subdirectory(iec61850_client_example_files)
|
||||
add_subdirectory(iec61850_client_example_reporting)
|
||||
add_subdirectory(iec61850_client_example_log)
|
||||
add_subdirectory(mms_client_example1)
|
||||
add_subdirectory(mms_client_example2)
|
||||
add_subdirectory(mms_client_example3)
|
||||
|
|
17
examples/iec61850_client_example_log/CMakeLists.txt
Normal file
17
examples/iec61850_client_example_log/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
|
||||
set(iec61850_client_example_log_SRCS
|
||||
client_example_log.c
|
||||
)
|
||||
|
||||
IF(MSVC)
|
||||
set_source_files_properties(${iec61850_client_example_log_SRCS}
|
||||
PROPERTIES LANGUAGE CXX)
|
||||
ENDIF(MSVC)
|
||||
|
||||
add_executable(iec61850_client_example_log
|
||||
${iec61850_client_example_log_SRCS}
|
||||
)
|
||||
|
||||
target_link_libraries(iec61850_client_example_log
|
||||
iec61850
|
||||
)
|
17
examples/iec61850_client_example_log/Makefile
Normal file
17
examples/iec61850_client_example_log/Makefile
Normal file
|
@ -0,0 +1,17 @@
|
|||
LIBIEC_HOME=../..
|
||||
|
||||
PROJECT_BINARY_NAME = client_example_log
|
||||
PROJECT_SOURCES = client_example_log.c
|
||||
|
||||
include $(LIBIEC_HOME)/make/target_system.mk
|
||||
include $(LIBIEC_HOME)/make/stack_includes.mk
|
||||
|
||||
all: $(PROJECT_BINARY_NAME)
|
||||
|
||||
include $(LIBIEC_HOME)/make/common_targets.mk
|
||||
|
||||
$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(PROJECT_BINARY_NAME)
|
133
examples/iec61850_client_example_log/client_example_log.c
Normal file
133
examples/iec61850_client_example_log/client_example_log.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* client_example_log.c
|
||||
*
|
||||
* This example is intended to be used with server_example_logging
|
||||
*/
|
||||
|
||||
#include "iec61850_client.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
//#include "hal_thread.h"
|
||||
|
||||
static void
|
||||
printJournalEntries(LinkedList journalEntries)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
LinkedList journalEntriesElem = LinkedList_getNext(journalEntries);
|
||||
|
||||
while (journalEntriesElem != NULL) {
|
||||
|
||||
MmsJournalEntry journalEntry = (MmsJournalEntry) LinkedList_getData(journalEntriesElem);
|
||||
|
||||
MmsValue_printToBuffer(MmsJournalEntry_getEntryID(journalEntry), buf, 1024);
|
||||
printf("EntryID: %s\n", buf);
|
||||
MmsValue_printToBuffer(MmsJournalEntry_getOccurenceTime(journalEntry), buf, 1024);
|
||||
printf(" occurence time: %s\n", buf);
|
||||
|
||||
LinkedList journalVariableElem = LinkedList_getNext(journalEntry->journalVariables);
|
||||
|
||||
while (journalVariableElem != NULL) {
|
||||
|
||||
MmsJournalVariable journalVariable = (MmsJournalVariable) LinkedList_getData(journalVariableElem);
|
||||
|
||||
printf(" variable-tag: %s\n", MmsJournalVariable_getTag(journalVariable));
|
||||
MmsValue_printToBuffer(MmsJournalVariable_getValue(journalVariable), buf, 1024);
|
||||
printf(" variable-value: %s\n", buf);
|
||||
|
||||
journalVariableElem = LinkedList_getNext(journalVariableElem);
|
||||
}
|
||||
|
||||
journalEntriesElem = LinkedList_getNext(journalEntriesElem);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
char* hostname;
|
||||
int tcpPort = 102;
|
||||
|
||||
if (argc > 1)
|
||||
hostname = argv[1];
|
||||
else
|
||||
hostname = "localhost";
|
||||
|
||||
if (argc > 2)
|
||||
tcpPort = atoi(argv[2]);
|
||||
|
||||
char* logRef = "simpleIOGenericIO/LLN0$EventLog";
|
||||
|
||||
IedClientError error;
|
||||
|
||||
IedConnection con = IedConnection_create();
|
||||
|
||||
IedConnection_connect(con, &error, hostname, tcpPort);
|
||||
|
||||
if (error == IED_ERROR_OK) {
|
||||
|
||||
/* read list of logs in LN (optional - if you don't know the existing logs) */
|
||||
LinkedList logs = IedConnection_getLogicalNodeDirectory(con, &error, "simpleIOGenericIO/LLN0", ACSI_CLASS_LOG);
|
||||
|
||||
if (error == IED_ERROR_OK) {
|
||||
printf("Found log in LN simpleIOGenericIO/LLN0:\n");
|
||||
|
||||
LinkedList log = LinkedList_getNext(logs);
|
||||
|
||||
while (log != NULL) {
|
||||
char* logName = (char*) LinkedList_getData(log);
|
||||
|
||||
printf(" %s\n", logName);
|
||||
|
||||
log = LinkedList_getNext(log);
|
||||
}
|
||||
|
||||
LinkedList_destroy(logs);
|
||||
}
|
||||
|
||||
/* read log control block (using the generic read function) */
|
||||
MmsValue* lcbValue = IedConnection_readObject(con, &error, "simpleIOGenericIO/LLN0.EventLog", IEC61850_FC_LG);
|
||||
|
||||
if (error == IED_ERROR_OK) {
|
||||
|
||||
MmsValue* oldEntryTm = MmsValue_getElement(lcbValue, 3);
|
||||
MmsValue* oldEntry = MmsValue_getElement(lcbValue, 5);
|
||||
|
||||
uint64_t timestamp = MmsValue_getUtcTimeInMs(oldEntryTm);
|
||||
|
||||
bool moreFollows;
|
||||
|
||||
/*
|
||||
* read the log contents. Be aware that the logRef uses the '$' sign as separator between the LN and
|
||||
* the log name! This is in contrast to the LCB object reference above.
|
||||
*/
|
||||
LinkedList logEntries = IedConnection_queryLogAfter(con, &error, "simpleIOGenericIO/LLN0$EventLog", oldEntry, timestamp, &moreFollows);
|
||||
|
||||
if (error == IED_ERROR_OK) {
|
||||
printJournalEntries(logEntries);
|
||||
|
||||
LinkedList_destroyDeep(logEntries, (LinkedListValueDeleteFunction) MmsJournalEntry_destroy);
|
||||
}
|
||||
else
|
||||
printf("QueryLog failed (error code: %i)!\n", error);
|
||||
|
||||
//TODO handle moreFollows
|
||||
|
||||
MmsValue_delete(lcbValue);
|
||||
}
|
||||
else
|
||||
printf("Read LCB failed!\n");
|
||||
|
||||
|
||||
IedConnection_abort(con, &error);
|
||||
}
|
||||
else {
|
||||
printf("Failed to connect to %s:%i\n", hostname, tcpPort);
|
||||
}
|
||||
|
||||
IedConnection_destroy(con);
|
||||
}
|
||||
|
||||
|
|
@ -2250,6 +2250,91 @@ exit_function:
|
|||
return dataSet;
|
||||
}
|
||||
|
||||
LinkedList /* <MmsJournalEntry> */
|
||||
IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const char* logReference,
|
||||
uint64_t startTime, uint64_t endTime, bool* moreFollows)
|
||||
{
|
||||
MmsError mmsError;
|
||||
|
||||
char logRef[130];
|
||||
|
||||
strncpy(logRef, logReference, 129);
|
||||
|
||||
char* logDomain = logRef;
|
||||
char* logName = strchr(logRef, '/');
|
||||
|
||||
if (logName != NULL) {
|
||||
|
||||
logName[0] = 0;
|
||||
logName++;
|
||||
|
||||
MmsValue* startTimeMms = MmsValue_newBinaryTime(false);
|
||||
MmsValue_setBinaryTime(startTimeMms, startTime);
|
||||
|
||||
MmsValue* endTimeMms = MmsValue_newBinaryTime(false);
|
||||
MmsValue_setBinaryTime(endTimeMms, endTime);
|
||||
|
||||
LinkedList journalEntries = MmsConnection_readJournalTimeRange(self->connection, &mmsError, logDomain, logName,
|
||||
startTimeMms, endTimeMms, moreFollows);
|
||||
|
||||
MmsValue_delete(startTimeMms);
|
||||
MmsValue_delete(endTimeMms);
|
||||
|
||||
if (mmsError != MMS_ERROR_NONE) {
|
||||
*error = iedConnection_mapMmsErrorToIedError(mmsError);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
return journalEntries;
|
||||
}
|
||||
else {
|
||||
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
LinkedList /* <MmsJournalEntry> */
|
||||
IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const char* logReference,
|
||||
MmsValue* entryID, uint64_t timeStamp, bool* moreFollows)
|
||||
{
|
||||
MmsError mmsError;
|
||||
|
||||
char logRef[130];
|
||||
|
||||
strncpy(logRef, logReference, 129);
|
||||
|
||||
char* logDomain = logRef;
|
||||
char* logName = strchr(logRef, '/');
|
||||
|
||||
if (logName != NULL) {
|
||||
|
||||
logName[0] = 0;
|
||||
logName++;
|
||||
|
||||
MmsValue* timeStampMms = MmsValue_newBinaryTime(false);
|
||||
MmsValue_setBinaryTime(timeStampMms, timeStamp);
|
||||
|
||||
LinkedList journalEntries = MmsConnection_readJournalStartAfter(self->connection, &mmsError, logDomain, logName,
|
||||
timeStampMms, entryID, moreFollows);
|
||||
|
||||
MmsValue_delete(timeStampMms);
|
||||
|
||||
if (mmsError != MMS_ERROR_NONE) {
|
||||
*error = iedConnection_mapMmsErrorToIedError(mmsError);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
return journalEntries;
|
||||
}
|
||||
else {
|
||||
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
MmsConnection
|
||||
IedConnection_getMmsConnection(IedConnection self)
|
||||
|
|
|
@ -1747,6 +1747,58 @@ MmsVariableSpecification*
|
|||
IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* dataAttributeReference,
|
||||
FunctionalConstraint fc);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup IEC61850_CLIENT_LOG_SERVICE Log service related functions, data types, and definitions
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Implementation of the QueryLogByTime ACSI service
|
||||
*
|
||||
* Read log entries from a log at the server. The log entries to read are specified by
|
||||
* a starting time and an end time. If the complete range does not fit in a single MMS message
|
||||
* the moreFollows flag will be set to true, to indicate that more entries are available for the
|
||||
* specified time range.
|
||||
*
|
||||
* \param self the connection object
|
||||
* \param error the error code if an error occurs
|
||||
* \param logReference log object reference in the form <LD name>/<LN name>$<log name>
|
||||
* \param startTime as millisecond UTC timestamp
|
||||
* \param endTime as millisecond UTC timestamp
|
||||
* \param moreFollows (output value) indicates that more entries are available that match the specification.
|
||||
*
|
||||
* \return list of MmsJournalEntry objects matching the specification
|
||||
*/
|
||||
LinkedList /* <MmsJournalEntry> */
|
||||
IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const char* logReference,
|
||||
uint64_t startTime, uint64_t endTime, bool* moreFollows);
|
||||
|
||||
/**
|
||||
* \brief Implementation of the QueryLogAfter ACSI service
|
||||
*
|
||||
* Read log entries from a log at the server following the entry with the specified entryID and timestamp.
|
||||
* If the complete range does not fit in a single MMS message
|
||||
* the moreFollows flag will be set to true, to indicate that more entries are available for the
|
||||
* specified time range.
|
||||
*
|
||||
* \param self the connection object
|
||||
* \param error the error code if an error occurs
|
||||
* \param logReference log object reference in the form <LD name>/<LN name>$<log name>
|
||||
* \param entryID usually the entryID of the last received entry
|
||||
* \param timeStamp as millisecond UTC timestamp
|
||||
* \param moreFollows (output value) indicates that more entries are available that match the specification.
|
||||
*
|
||||
* \return list of MmsJournalEntry objects matching the specification
|
||||
*/
|
||||
LinkedList /* <MmsJournalEntry> */
|
||||
IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const char* logReference,
|
||||
MmsValue* entryID, uint64_t timeStamp, bool* moreFollows);
|
||||
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
|
|
@ -320,7 +320,8 @@ mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, Linked
|
|||
|
||||
tag = buffer[bufPos++];
|
||||
|
||||
*moreFollows = false;
|
||||
if (moreFollows)
|
||||
*moreFollows = false;
|
||||
|
||||
if (tag != 0x41) {
|
||||
if (DEBUG_MMS_CLIENT)
|
||||
|
@ -355,7 +356,8 @@ mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, Linked
|
|||
break;
|
||||
|
||||
case 0x81: /* moreFollows */
|
||||
*moreFollows = BerDecoder_decodeBoolean(buffer, bufPos);
|
||||
if (moreFollows)
|
||||
*moreFollows = BerDecoder_decodeBoolean(buffer, bufPos);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -373,51 +375,6 @@ mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, Linked
|
|||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
mmsClient_createReadJournalRequest(uint32_t invokeId, ByteBuffer* request, const char* domainId, const char* itemId)
|
||||
{
|
||||
/* calculate sizes */
|
||||
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId);
|
||||
|
||||
uint32_t domainIdStringSize = strlen(domainId);
|
||||
uint32_t domainIdSize = 1 + BerEncoder_determineLengthSize(domainIdStringSize) + domainIdStringSize;
|
||||
|
||||
uint32_t itemIdStringSize = strlen(itemId);
|
||||
uint32_t itemIdSize = 1 + BerEncoder_determineLengthSize(itemIdStringSize) + itemIdStringSize;
|
||||
|
||||
uint32_t objectIdSize = domainIdSize + itemIdSize;
|
||||
|
||||
uint32_t journalNameSize = 1 + BerEncoder_determineLengthSize(objectIdSize) + (objectIdSize);
|
||||
|
||||
uint32_t journalReadSize = 1 + BerEncoder_determineLengthSize(journalNameSize) + (journalNameSize);
|
||||
|
||||
uint32_t confirmedRequestPduSize = 1 + 2 + 2 + invokeIdSize + journalReadSize;
|
||||
|
||||
/* encode to buffer */
|
||||
int bufPos = 0;
|
||||
uint8_t* buffer = request->buffer;
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0xa0, confirmedRequestPduSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
|
||||
|
||||
/* Encode read journal tag (context | structured ) [65 = 41h] */
|
||||
buffer[bufPos++] = 0xbf;
|
||||
buffer[bufPos++] = 0x41;
|
||||
|
||||
bufPos = BerEncoder_encodeLength(journalReadSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeTL(0xa0, journalNameSize, buffer, bufPos);
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0xa1, objectIdSize, buffer, bufPos);
|
||||
|
||||
bufPos = BerEncoder_encodeOctetString(0x1a, (uint8_t*) domainId, domainIdStringSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeOctetString(0x1a, (uint8_t*) itemId, itemIdStringSize, buffer, bufPos);
|
||||
|
||||
request->size = bufPos;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
mmsClient_createReadJournalRequestWithTimeRange(uint32_t invokeId, ByteBuffer* request, const char* domainId, const char* itemId,
|
||||
MmsValue* startingTime, MmsValue* endingTime)
|
||||
|
|
|
@ -69,10 +69,8 @@ entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFo
|
|||
|
||||
if (moreFollow) {
|
||||
|
||||
if (encoder->moreFollows) {
|
||||
printf("entryCallback return false\n");
|
||||
if (encoder->moreFollows)
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder->currentEntryBufPos = encoder->bufPos;
|
||||
|
||||
|
@ -80,10 +78,6 @@ entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFo
|
|||
|
||||
encoder->currentEntryId = entryID;
|
||||
encoder->currentTimestamp = timestamp;
|
||||
|
||||
}
|
||||
else {
|
||||
printf("Encoded last entry: FINISHED\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -319,9 +313,6 @@ mmsServer_handleReadJournalRequest(
|
|||
|
||||
memcpy(rangeStart.value.binaryTime.buf, requestBuffer + bufPos, length);
|
||||
|
||||
char stringBuf[100];
|
||||
MmsValue_printToBuffer(&rangeStart, stringBuf, 100);
|
||||
|
||||
hasRangeStartSpec = true;
|
||||
}
|
||||
else {
|
||||
|
@ -350,9 +341,6 @@ mmsServer_handleReadJournalRequest(
|
|||
|
||||
memcpy(rangeStop.value.binaryTime.buf, requestBuffer + bufPos, length);
|
||||
|
||||
char stringBuf[100];
|
||||
MmsValue_printToBuffer(&rangeStop, stringBuf, 100);
|
||||
|
||||
hasRangeStopSpec = true;
|
||||
}
|
||||
else {
|
||||
|
@ -382,9 +370,6 @@ mmsServer_handleReadJournalRequest(
|
|||
|
||||
memcpy(rangeStart.value.binaryTime.buf, requestBuffer + bufPos, length);
|
||||
|
||||
char stringBuf[100];
|
||||
MmsValue_printToBuffer(&rangeStart, stringBuf, 100);
|
||||
|
||||
hasTimeSpec = true;
|
||||
}
|
||||
else {
|
||||
|
@ -428,7 +413,9 @@ mmsServer_handleReadJournalRequest(
|
|||
|
||||
//TODO check if required fields are present
|
||||
if (hasNames == false) {
|
||||
printf("MMS_SERVER: readJournal missing journal name\n");
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal missing journal name\n");
|
||||
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return;
|
||||
}
|
||||
|
@ -441,7 +428,9 @@ mmsServer_handleReadJournalRequest(
|
|||
MmsDomain* mmsDomain = MmsDevice_getDomain(mmsDevice, domainId);
|
||||
|
||||
if (mmsDomain == NULL) {
|
||||
printf("Domain %s not found\n", domainId);
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal domain %s not found\n", domainId);
|
||||
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return;
|
||||
}
|
||||
|
@ -449,12 +438,15 @@ mmsServer_handleReadJournalRequest(
|
|||
MmsJournal mmsJournal = MmsDomain_getJournal(mmsDomain, logName);
|
||||
|
||||
if (mmsJournal == NULL) {
|
||||
printf("Journal %s not found\n", logName);
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal journal %s not found\n", logName);
|
||||
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Read journal %s ...\n", mmsJournal->name);
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name);
|
||||
|
||||
struct sJournalEncoder encoder;
|
||||
|
||||
|
@ -476,7 +468,9 @@ mmsServer_handleReadJournalRequest(
|
|||
entryCallback, entryDataCallback, &encoder);
|
||||
}
|
||||
else {
|
||||
printf("MMS_SERVER: readJournal missing valid argument combination\n");
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal missing valid argument combination\n");
|
||||
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -545,3 +545,5 @@ EXPORTS
|
|||
MmsConnection_readJournalStartAfter
|
||||
LogControlBlock_create
|
||||
Log_create
|
||||
IedConnection_queryLogByTime
|
||||
IedConnection_queryLogAfter
|
||||
|
|
|
@ -621,3 +621,6 @@ EXPORTS
|
|||
MmsConnection_readJournalStartAfter
|
||||
LogControlBlock_create
|
||||
Log_create
|
||||
IedConnection_queryLogByTime
|
||||
IedConnection_queryLogAfter
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue