MMS client: added asynchronous read journal service

pull/93/head
Michael Zillgith 7 years ago
parent c80a87d5e8
commit fc1d62d1a7

@ -29,6 +29,30 @@ reportCallbackFunction(void* parameter, ClientReport report)
}
}
static void
printValue(char* name, MmsValue* value)
{
char buf[1000];
MmsValue_printToBuffer(value, buf, 1000);
printf("Received value for %s: %s\n", name, buf);
}
static void
readVariableHandler (int invokeId, void* parameter, MmsError mmsError, MmsValue* value)
{
if (mmsError == MMS_ERROR_NONE) {
printValue((char*) parameter, value);
MmsValue_delete(value);
}
else {
printf("Failed to read %s (err=%i)\n", parameter, mmsError);
}
}
int main(int argc, char** argv) {
char* hostname;
@ -50,76 +74,171 @@ int main(int argc, char** argv) {
if (error == IED_ERROR_OK) {
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);
MmsConnection mmsCon = IedConnection_getMmsConnection(con);
if (value != NULL) {
float fval = MmsValue_toFloat(value);
printf("read float value: %f\n", fval);
MmsValue_delete(value);
}
/* write a variable to the server */
value = MmsValue_newVisibleString("libiec61850.com");
IedConnection_writeObject(con, &error, "simpleIOGenericIO/GGIO1.NamPlt.vendor", IEC61850_FC_DC, value);
MmsError mmsErr;
if (error != IED_ERROR_OK)
printf("failed to write simpleIOGenericIO/GGIO1.NamPlt.vendor!\n");
MmsValue* val1 = MmsConnection_readVariable(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn1$mag$f");
MmsValue_delete(value);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 0 failed\n");
else
printValue("sync read", val1);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn1$mag$f", readVariableHandler, "GGIO1$MX$AnIn1$mag$f");
/* read data set */
ClientDataSet clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", NULL);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 1 failed\n");
if (clientDataSet == NULL)
printf("failed to read dataset\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn2$mag$f", readVariableHandler, "GGIO1$MX$AnIn2$mag$f");
/* Read RCB values */
ClientReportControlBlock rcb =
IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 2 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn3$mag$f", readVariableHandler, "GGIO1$MX$AnIn3$mag$f");
bool rptEna = ClientReportControlBlock_getRptEna(rcb);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 3 failed\n");
printf("RptEna = %i\n", rptEna);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn4$mag$f", readVariableHandler, "GGIO1$MX$AnIn4$mag$f");
/* Install handler for reports */
IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB01",
ClientReportControlBlock_getRptId(rcb), reportCallbackFunction, NULL);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 4 failed\n");
/* Set trigger options and enable report */
ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY | TRG_OPT_GI);
ClientReportControlBlock_setRptEna(rcb, true);
ClientReportControlBlock_setIntgPd(rcb, 5000);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD, true);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn1$mag$f", readVariableHandler, "GGIO1$MX$AnIn1$mag$f");
if (error != IED_ERROR_OK)
printf("report activation failed (code: %i)\n", error);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 1 failed\n");
Thread_sleep(1000);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn2$mag$f", readVariableHandler, "GGIO1$MX$AnIn2$mag$f");
/* trigger GI report */
ClientReportControlBlock_setGI(rcb, true);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 2 failed\n");
if (error != IED_ERROR_OK)
printf("Error triggering a GI report (code: %i)\n", error);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn3$mag$f", readVariableHandler, "GGIO1$MX$AnIn3$mag$f");
Thread_sleep(60000);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 3 failed\n");
/* disable reporting */
ClientReportControlBlock_setRptEna(rcb, false);
IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA, true);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn4$mag$f", readVariableHandler, "GGIO1$MX$AnIn4$mag$f");
if (error != IED_ERROR_OK)
printf("disable reporting failed (code: %i)\n", error);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 4 failed\n");
ClientDataSet_destroy(clientDataSet);
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn1$mag$f", readVariableHandler, "GGIO1$MX$AnIn1$mag$f");
ClientReportControlBlock_destroy(rcb);
if (mmsErr != MMS_ERROR_NONE)
printf("Request 1 failed\n");
close_connection:
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn2$mag$f", readVariableHandler, "GGIO1$MX$AnIn2$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 2 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn3$mag$f", readVariableHandler, "GGIO1$MX$AnIn3$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 3 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn4$mag$f", readVariableHandler, "GGIO1$MX$AnIn4$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 4 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn1$mag$f", readVariableHandler, "GGIO1$MX$AnIn1$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 1 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn2$mag$f", readVariableHandler, "GGIO1$MX$AnIn2$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 2 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn3$mag$f", readVariableHandler, "GGIO1$MX$AnIn3$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 3 failed\n");
MmsConnection_readVariableAsync(mmsCon, &mmsErr, "simpleIOGenericIO", "GGIO1$MX$AnIn4$mag$f", readVariableHandler, "GGIO1$MX$AnIn4$mag$f");
if (mmsErr != MMS_ERROR_NONE)
printf("Request 4 failed\n");
Thread_sleep(10000);
// /* read an analog measurement value from server */
// MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);
//
// if (value != NULL) {
// float fval = MmsValue_toFloat(value);
// printf("read float value: %f\n", fval);
// MmsValue_delete(value);
// }
//
// /* write a variable to the server */
// value = MmsValue_newVisibleString("libiec61850.com");
// IedConnection_writeObject(con, &error, "simpleIOGenericIO/GGIO1.NamPlt.vendor", IEC61850_FC_DC, value);
//
// if (error != IED_ERROR_OK)
// printf("failed to write simpleIOGenericIO/GGIO1.NamPlt.vendor!\n");
//
// MmsValue_delete(value);
//
//
// /* read data set */
// ClientDataSet clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", NULL);
//
// if (clientDataSet == NULL)
// printf("failed to read dataset\n");
//
// /* Read RCB values */
// ClientReportControlBlock rcb =
// IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL);
//
//
// bool rptEna = ClientReportControlBlock_getRptEna(rcb);
//
// printf("RptEna = %i\n", rptEna);
//
// /* Install handler for reports */
// IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB01",
// ClientReportControlBlock_getRptId(rcb), reportCallbackFunction, NULL);
//
// /* Set trigger options and enable report */
// ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_UPDATE | TRG_OPT_INTEGRITY | TRG_OPT_GI);
// ClientReportControlBlock_setRptEna(rcb, true);
// ClientReportControlBlock_setIntgPd(rcb, 5000);
// IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_INTG_PD, true);
//
// if (error != IED_ERROR_OK)
// printf("report activation failed (code: %i)\n", error);
//
// Thread_sleep(1000);
//
// /* trigger GI report */
// ClientReportControlBlock_setGI(rcb, true);
// IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_GI, true);
//
// if (error != IED_ERROR_OK)
// printf("Error triggering a GI report (code: %i)\n", error);
//
// Thread_sleep(60000);
//
// /* disable reporting */
// ClientReportControlBlock_setRptEna(rcb, false);
// IedConnection_setRCBValues(con, &error, rcb, RCB_ELEMENT_RPT_ENA, true);
//
// if (error != IED_ERROR_OK)
// printf("disable reporting failed (code: %i)\n", error);
//
// ClientDataSet_destroy(clientDataSet);
//
// ClientReportControlBlock_destroy(rcb);
//
// close_connection:
IedConnection_close(con);
}

@ -1,7 +1,7 @@
/*
* mms_client_connection.h
*
* Copyright 2013-2016 Michael Zillgith
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -1036,15 +1036,26 @@ MmsJournalVariable_getTag(MmsJournalVariable self);
const MmsValue*
MmsJournalVariable_getValue(MmsJournalVariable self);
typedef void
(*MmsConnection_ReadJournalHandler) (int invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsJournalEntry> */ journalEntries, bool moreFollows);
LinkedList
LinkedList /* <MmsJournalEntry> */
MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* startingTime, MmsValue* endingTime, bool* moreFollows);
MmsValue* startTime, MmsValue* endTime, bool* moreFollows);
uint32_t
MmsConnection_readJournalTimeRangeAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter);
LinkedList
LinkedList /* <MmsJournalEntry> */
MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* timeSpecification, MmsValue* entrySpecification, bool* moreFollows);
uint32_t
MmsConnection_readJournalStartAfterAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter);
/**
* \brief Destroy (free) an MmsServerIdentity object
*

@ -72,7 +72,8 @@ typedef enum {
MMS_CALL_TYPE_DELETE_NVL,
MMS_CALL_TYPE_GET_VAR_ACCESS_ATTR,
MMS_CALL_TYPE_GET_SERVER_STATUS,
MMS_CALL_TYPE_IDENTIFY
MMS_CALL_TYPE_IDENTIFY,
MMS_CALL_TYPE_READ_JOURNAL
} eMmsOutstandingCallType;
struct sMmsOutstandingCall
@ -331,7 +332,8 @@ mmsClient_createReadJournalRequestStartAfter(uint32_t invokeId, ByteBuffer* requ
MmsValue* timeSpecification, MmsValue* entrySpecification);
bool
mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, LinkedList* result);
mmsClient_parseReadJournalResponse(MmsConnection self, ByteBuffer* response, int respBufPos, bool* moreFollows, LinkedList* result);
void
mmsClient_handleFileOpenRequest(MmsConnection connection,

@ -949,6 +949,26 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M
}
}
else if (outstandingCall->type == MMS_CALL_TYPE_READ_JOURNAL) {
MmsConnection_ReadJournalHandler handler =
(MmsConnection_ReadJournalHandler) outstandingCall->userCallback;
if (err != MMS_ERROR_NONE) {
handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false);
}
else {
bool moreFollows = false;
LinkedList entries = NULL;
if (mmsClient_parseReadJournalResponse(self, response, bufPos, &moreFollows, &entries) == false) {
handler(outstandingCall->invokeId, outstandingCall->userParameter, MMS_ERROR_PARSING_RESPONSE, NULL, false);
}
else {
handler(outstandingCall->invokeId, outstandingCall->userParameter, err, entries, moreFollows);
}
}
}
removeFromOutstandingCalls(self, outstandingCall->invokeId);
@ -964,7 +984,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload)
printf("MMS_CLIENT: mmsIsoCallback called with indication %i\n", indication);
if (indication == ISO_IND_TICK) {
//TODO check timeouts
/* check timeouts */
uint64_t currentTime = Hal_getTimeInMs();
@ -2833,7 +2854,6 @@ MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLo
if (mmsError)
*mmsError = err;
}
uint32_t
@ -2863,25 +2883,6 @@ exit_function:
return invokeId;
}
static LinkedList
readJournal(MmsConnection self, MmsError* mmsError, uint32_t invokeId, ByteBuffer* payload, bool* moreFollows)
{
ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload, mmsError);
LinkedList response = NULL;
if (responseMessage != NULL) {
if (mmsClient_parseReadJournalResponse(self, moreFollows, &response) == false)
*mmsError = MMS_ERROR_PARSING_RESPONSE;
}
releaseResponse(self);
return response;
}
static void
MmsJournalVariable_destroy(MmsJournalVariable self)
{
@ -2934,45 +2935,162 @@ MmsJournalVariable_getValue(MmsJournalVariable self)
return self->value;
}
struct readJournalParameters
{
Semaphore waitForResponse;
MmsError err;
LinkedList entries;
bool moreFollows;
};
static void
readJournalHandler(int invokeId, void* parameter, MmsError mmsError, LinkedList entries, bool moreFollows)
{
struct readJournalParameters* parameters = (struct readJournalParameters*) parameter;
parameters->err = mmsError;
parameters->entries = entries;
parameters->moreFollows = moreFollows;
/* unblock user thread */
Semaphore_post(parameters->waitForResponse);
}
LinkedList
MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* startingTime, MmsValue* endingTime, bool* moreFollows)
MmsValue* startTime, MmsValue* endTime, bool* moreFollows)
{
struct readJournalParameters parameter;
MmsError err = MMS_ERROR_NONE;
parameter.waitForResponse = Semaphore_create(1);
parameter.err = MMS_ERROR_NONE;
parameter.entries = NULL;
parameter.moreFollows = false;
Semaphore_wait(parameter.waitForResponse);
MmsConnection_readJournalTimeRangeAsync(self, &err, domainId, itemId, startTime, endTime, readJournalHandler, &parameter);
if (err == MMS_ERROR_NONE) {
Semaphore_wait(parameter.waitForResponse);
err = parameter.err;
if (moreFollows)
*moreFollows = parameter.moreFollows;
}
Semaphore_destroy(parameter.waitForResponse);
if (mmsError)
*mmsError = err;
return parameter.entries;
}
uint32_t
MmsConnection_readJournalTimeRangeAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter)
{
if ((MmsValue_getType(startingTime) != MMS_BINARY_TIME) ||
(MmsValue_getType(endingTime) != MMS_BINARY_TIME)) {
uint32_t invokeId = 0;
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
if (mmsError)
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
if ((MmsValue_getType(startTime) != MMS_BINARY_TIME) ||
(MmsValue_getType(endTime) != MMS_BINARY_TIME)) {
*mmsError = MMS_ERROR_INVALID_ARGUMENTS;
return NULL;
goto exit_function;
}
ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
uint32_t invokeId = getNextInvokeId(self);
invokeId = getNextInvokeId(self);
mmsClient_createReadJournalRequestWithTimeRange(invokeId, payload, domainId, itemId, startingTime, endingTime);
mmsClient_createReadJournalRequestWithTimeRange(invokeId, payload, domainId, itemId, startTime, endTime);
return readJournal(self, mmsError, invokeId, payload, moreFollows);
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_JOURNAL, handler, parameter);
if (mmsError)
*mmsError = err;
exit_function:
return invokeId;
}
LinkedList
MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* timeSpecification, MmsValue* entrySpecification, bool* moreFollows)
{
struct readJournalParameters parameter;
MmsError err = MMS_ERROR_NONE;
parameter.waitForResponse = Semaphore_create(1);
parameter.err = MMS_ERROR_NONE;
parameter.entries = NULL;
parameter.moreFollows = false;
Semaphore_wait(parameter.waitForResponse);
MmsConnection_readJournalStartAfterAsync(self, &err, domainId, itemId, timeSpecification, entrySpecification, readJournalHandler, &parameter);
if (err == MMS_ERROR_NONE) {
Semaphore_wait(parameter.waitForResponse);
err = parameter.err;
if (moreFollows)
*moreFollows = parameter.moreFollows;
}
Semaphore_destroy(parameter.waitForResponse);
if (mmsError)
*mmsError = err;
return parameter.entries;
}
uint32_t
MmsConnection_readJournalStartAfterAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter)
{
uint32_t invokeId = 0;
if (getAssociationState(self) != MMS_STATE_CONNECTED) {
if (mmsError)
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
if ((MmsValue_getType(timeSpecification) != MMS_BINARY_TIME) ||
(MmsValue_getType(entrySpecification) != MMS_OCTET_STRING)) {
*mmsError = MMS_ERROR_INVALID_ARGUMENTS;
return NULL;
goto exit_function;
}
ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
uint32_t invokeId = getNextInvokeId(self);
invokeId = getNextInvokeId(self);
mmsClient_createReadJournalRequestStartAfter(invokeId, payload, domainId, itemId, timeSpecification, entrySpecification);
return readJournal(self, mmsError, invokeId, payload, moreFollows);
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_JOURNAL, handler, parameter);
if (mmsError)
*mmsError = err;
exit_function:
return invokeId;
}
int32_t

@ -50,11 +50,12 @@ mmsClient_createIdentifyRequest(uint32_t invokeId, ByteBuffer* request)
}
bool
mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, uint32_t invokeId, MmsConnection_IdentifyHandler handler, void* parameter)
mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32_t respBufPos, uint32_t invokeId, MmsConnection_IdentifyHandler handler, void* parameter)
{
uint8_t* buffer = ByteBuffer_getBuffer(response);
int maxBufPos = ByteBuffer_getSize(response);
int length;
int bufPos = (int) respBufPos;
uint8_t tag = buffer[bufPos++];
if (tag != 0xa2)
@ -107,12 +108,6 @@ mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32
}
}
// identityInfo = (MmsServerIdentity*) GLOBAL_MALLOC(sizeof(MmsServerIdentity));
//
// identityInfo->vendorName = vendorName;
// identityInfo->modelName = modelName;
// identityInfo->revision = revision;
handler(invokeId, parameter, MMS_ERROR_NONE, vendorName, modelName, revision);
return true;

@ -305,11 +305,11 @@ parseListOfJournalEntries(uint8_t* buffer, int bufPos, int maxLength, LinkedList
}
bool
mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows, LinkedList* result)
mmsClient_parseReadJournalResponse(MmsConnection self, ByteBuffer* response, int respBufPos, bool* moreFollows, LinkedList* result)
{
uint8_t* buffer = self->lastResponse->buffer;
int maxBufPos = self->lastResponse->size;
int bufPos = self->lastResponseBufPos;
uint8_t* buffer = ByteBuffer_getBuffer(response);
int maxBufPos = ByteBuffer_getSize(response);
int bufPos = respBufPos;
int length;
uint8_t tag = buffer[bufPos++];

Loading…
Cancel
Save