- implemented client and server side readJournal message parsing and generation

pull/6/head
Michael Zillgith 9 years ago
parent 7acd515a96
commit ba08a2ad59

@ -1,24 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL">
<Header id="" nameStructure="IEDName">
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B">
<Header id="" version="1" revision="" nameStructure="IEDName">
</Header>
<Communication>
<SubNetwork name="subnetwork1" type="8-MMS">
<Text>Station bus</Text>
<BitRate unit="b/s">10</BitRate>
<ConnectedAP iedName="beagle" apName="accessPoint1">
<Address>
<P type="IP">10.0.0.2</P>
<P type="IP-SUBNET">255.255.255.0</P>
<P type="IP-GATEWAY">10.0.0.1</P>
<P type="OSI-TSEL">0001</P>
<P type="OSI-PSEL">00000001</P>
<P type="OSI-SSEL">0001</P>
</Address>
</ConnectedAP>
</SubNetwork>
</Communication>
<IED name="beagle">
<IED name="Template">
<Services>
<DynAssociation max="5"/>
@ -30,17 +15,13 @@
<ReadWrite />
<GetCBValues />
<ConfDataSet max="1" modify="false" />
<DynDataSet max="10"/>
<ConfReportControl max="1" bufConf="false" />
<DynDataSet max="5"/>
<ConfReportControl max="5" bufConf="false" />
<ReportSettings cbName="Fix" datSet="Dyn" rptID="Dyn" optFields="Dyn" bufTime="Dyn" trgOps="Dyn" intgPd="Dyn" owner="true"/>
<ConfLNs fixPrefix="true" fixLnInst="true"/>
<ClientServices >
<TimeSyncProt sntp="true" c37_238="false" other="false"/>
</ClientServices>
<GOOSE max="5" />
<GSSE max="5" />
<GSEDir />
<TimerActivatedControl />
</Services>
<AccessPoint name="accessPoint1">
@ -63,22 +44,12 @@
<RptEnabled max="5" />
</ReportControl>
<DOI name="Mod">
<DAI name="ctlModel">
<Val>status-only</Val>
</DAI>
</DOI>
</LN0>
<LN lnClass="LPHD" lnType="LPHD1" inst="1" prefix="" />
<LN lnClass="GGIO" lnType="GGIO1" inst="1" prefix="">
<DOI name="Mod">
<DAI name="ctlModel">
<Val>status-only</Val>
</DAI>
</DOI>
<DOI name="SPCSO1">
<DAI name="ctlModel">
<Val>direct-with-normal-security</Val>
@ -86,7 +57,7 @@
</DOI>
<DOI name="SPCSO2">
<DAI name="ctlModel">
<Val>sbo-with-normal-security</Val>
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
<DOI name="SPCSO3">
@ -96,7 +67,7 @@
</DOI>
<DOI name="DPCSO1">
<DAI name="ctlModel">
<Val>direct-with-enhanced-security</Val>
<Val>direct-with-normal-security</Val>
</DAI>
</DOI>
</LN>
@ -112,20 +83,20 @@
<LNodeType id="LLN01" lnClass="LLN0">
<DO name="Beh" type="ENS_Beh" />
<DO name="Mod" type="ENC_Mod_status_only" />
<DO name="Health" type="ENS_Beh" />
<DO name="Health" type="ENS_Health" />
<DO name="NamPlt" type="LPL_1_NamPlt" />
</LNodeType>
<LNodeType id="LPHD1" lnClass="LPHD">
<DO name="PhyNam" type="DPL_1_PhyNam" />
<DO name="PhyHealth" type="ENS_Beh" />
<DO name="PhyHealth" type="ENS_Health" />
<DO name="Proxy" type="SPS_1_Proxy" />
</LNodeType>
<LNodeType id="GGIO1" lnClass="GGIO">
<DO name="Beh" type="ENS_Beh" />
<DO name="Mod" type="ENC_Mod_status_only" />
<DO name="Health" type="ENS_Beh" />
<DO name="Health" type="ENS_Health" />
<DO name="NamPlt" type="LPL_2_NamPlt" />
<DO name="AnIn1" type="MV_1_AnIn1" />
<DO name="AnIn2" type="MV_1_AnIn1" />
@ -167,16 +138,22 @@
<DA name="t" fc="ST" bType="Timestamp" />
</DOType>
<DOType id="ENS_Health" cdc="ENS">
<DA name="stVal" fc="ST" bType="Enum" type="healthEnum" dchg="true" />
<DA name="q" fc="ST" bType="Quality" qchg="true" />
<DA name="t" fc="ST" bType="Timestamp" />
</DOType>
<DOType id="INC_OpCntRs" cdc="INC">
<DA name="stVal" bType="INT32" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF">
<DA name="Oper" type="INCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" valKind="RO" bType="Enum" fc="CF">
<Val>direct-with-normal-security</Val>
</DA>
</DOType>
<DOType cdc="ACD" id="ACD_Str">
<DA bType="BOOLEAN" dchg="true" fc="ST" name="general" />
<DA bType="Enum" dchg="true" fc="ST" name="dirGeneral" type="dirGeneral" />
@ -194,7 +171,7 @@
<DA bType="Enum" dchg="true" fc="ST" name="stVal" type="Beh"/>
<DA bType="Quality" fc="ST" name="q" qchg="true"/>
<DA bType="Timestamp" fc="ST" name="t"/>
<DA bType="Enum" fc="CF" name="ctlModel" type="CtlModels">
<DA bType="Enum" fc="CF" name="ctlModel" valKind="RO" type="CtlModels">
<Val>status-only</Val>
</DA>
</DOType>
@ -233,31 +210,25 @@
<DA name="stVal" bType="Dbpos" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="INC_2_Mod" cdc="INC">
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="ctlModel" type="CtlModels" valKind="RO" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
</DOType>
<DOType id="SPC_2_SPCSO1" cdc="SPC">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="ctlModel" type="CtlModels" valKind="RO" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="SPC_1_SPCSO2" cdc="SPC">
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="SBO" fc="CO" bType="VisString64" />
<DA name="SBO" fc="CO" bType="VisString129" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="Cancel" fc="CO" bType="Struct" type="SPCCancel" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="ctlModel" type="CtlModels" valKind="RO" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
@ -265,15 +236,7 @@
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="SPCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
<DOType id="INC_OpCntRs" cdc="SPC">
<DA name="stVal" bType="INT32" fc="ST" dchg="true" />
<DA name="q" bType="Quality" fc="ST" qchg="true" />
<DA name="Oper" type="INCOperate_1" bType="Struct" fc="CO" />
<DA name="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
<DA name="ctlModel" type="CtlModels" valKind="RO" bType="Enum" fc="CF" />
<DA name="t" bType="Timestamp" fc="ST" />
</DOType>
@ -286,15 +249,6 @@
<BDA name="orIdent" bType="Octet64" />
</DAType>
<DAType id="INCOperate_1">
<BDA name="ctlVal" bType="INT32" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
<BDA name="Check" bType="Check" />
</DAType>
<DAType id="SPCOperate_1">
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="origin" type="Originator_1" bType="Struct" />
@ -305,12 +259,21 @@
</DAType>
<DAType id="SPCCancel">
<BDA name="ctlVal" bType="INT8" />
<BDA name="ctlVal" bType="BOOLEAN" />
<BDA name="origin" bType="Struct" type="Originator_1" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
</DAType>
<DAType id="INCOperate_1">
<BDA name="ctlVal" bType="INT32" />
<BDA name="origin" type="Originator_1" bType="Struct" />
<BDA name="ctlNum" bType="INT8U" />
<BDA name="T" bType="Timestamp" />
<BDA name="Test" bType="BOOLEAN" />
<BDA name="Check" bType="Check" />
</DAType>
<EnumType id="Beh">
<EnumVal ord="1">on</EnumVal>
@ -320,6 +283,12 @@
<EnumVal ord="5">off</EnumVal>
</EnumType>
<EnumType id="healthEnum">
<EnumVal ord="1">Ok</EnumVal>
<EnumVal ord="2">Warning</EnumVal>
<EnumVal ord="3">Alarm</EnumVal>
</EnumType>
<EnumType id="dirGeneral">
<EnumVal ord="0">unknown</EnumVal>
<EnumVal ord="1">forward</EnumVal>
@ -330,9 +299,6 @@
<EnumType id="CtlModels">
<EnumVal ord="0">status-only</EnumVal>
<EnumVal ord="1">direct-with-normal-security</EnumVal>
<EnumVal ord="2">sbo-with-normal-security</EnumVal>
<EnumVal ord="3">direct-with-enhanced-security</EnumVal>
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
</EnumType>
<EnumType id="OrCat">

@ -1114,7 +1114,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_SBO = {
NULL,
0,
IEC61850_FC_CO,
IEC61850_VISIBLE_STRING_64,
IEC61850_VISIBLE_STRING_129,
0,
NULL,
0};
@ -2404,6 +2404,8 @@ ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0,
IedModel iedModel = {
"beagle",
&iedModel_GenericIO,
@ -2412,6 +2414,8 @@ IedModel iedModel = {
NULL,
NULL,
NULL,
NULL,
NULL,
initializeValues
};
@ -2425,11 +2429,11 @@ iedModel_GenericIO_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0)
iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(2);
iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
iedModel_GenericIO_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
iedModel_GenericIO_GGIO1_DPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(3);
iedModel_GenericIO_GGIO1_DPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
iedModel_GenericIO_TIM_GAPC1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0);

@ -172,11 +172,11 @@ int main(int argc, char** argv) {
printf(" read journal...\n");
// MmsConnection_readJournal(con, &error, domainName, name);
#if 0
#if 1
uint64_t timestamp = Hal_getTimeInMs();
MmsValue* startTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(startTime, timestamp - 60000);
MmsValue_setBinaryTime(startTime, timestamp - 6000000000);
MmsValue* endTime = MmsValue_newBinaryTime(false);
MmsValue_setBinaryTime(endTime, timestamp);
@ -184,7 +184,7 @@ int main(int argc, char** argv) {
MmsConnection_readJournalTimeRange(con, &error, domainName, name, startTime, endTime);
#endif
#if 1
#if 0
uint64_t timestamp = Hal_getTimeInMs();
MmsValue* startTime = MmsValue_newBinaryTime(false);

@ -89,6 +89,7 @@ LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* valu
printf("no log storage available!\n");
}
void
LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage)
{
@ -100,7 +101,6 @@ LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage)
printf("Attached storage to log: %s\n", self->name);
printf(" oldEntryID: %llu oldEntryTm: %llu\n", self->oldEntryId, self->oldEntryTime);
printf(" newEntryID: %llu newEntryTm: %llu\n", self->newEntryId, self->newEntryTime);
}
LogControl*
@ -585,9 +585,51 @@ MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logSto
{
LogInstance* logInstance = getLogInstanceByLogRef(self, logRef);
if (logInstance != NULL)
if (logInstance != NULL) {
LogInstance_setLogStorage(logInstance, logStorage);
#if 1
char domainName[65];
MmsMapping_getMmsDomainFromObjectReference(logRef, domainName);
char domainName2[65];
strcpy(domainName2, self->model->name);
strcat(domainName2, domainName);
MmsDomain* mmsDomain = MmsDevice_getDomain(self->mmsDevice, domainName2);
if (mmsDomain == NULL) {
printf("IED_SERVER: MmsMapping_setLogStorage: domain %s not found!\n", domainName2);
}
#if 0
char journalName[65];
strcpy(journalName, self->parentLN->name);
strcat(journalName, "$");
strcat(journalName, self->name);
#endif
printf("Connect LogStorage to MMS journal %s\n", logRef);
MmsJournal mmsJournal = NULL;
char* logName = strchr(logRef, '/');
if (logName != NULL) {
logName += 1;
mmsJournal = MmsDomain_getJournal(mmsDomain, logName);
}
if (mmsJournal != NULL)
mmsJournal->logStorage = logStorage;
else
printf("Failed to retrieve MMS journal for log!\n");
#endif
}
//if (DEBUG_IED_SERVER)
if (logInstance == NULL)
printf("IED_SERVER: MmsMapping_setLogStorage no matching log for %s found!\n", logRef);

@ -43,20 +43,15 @@ struct sLogStorage {
void* instanceData;
LogEntryCallback entryCallback;
LogEntryDataCallback entryDataCallback;
void* callbackParameter;
// bool (*initializeLog) (const char* logName, int logSize);
uint64_t (*addEntry) (LogStorage self, uint64_t timestamp);
bool (*addEntryData) (LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
bool (*getEntries) (LogStorage self, uint64_t startingTime, uint64_t endingTime);
bool (*getEntries) (LogStorage self, uint64_t startingTime, uint64_t endingTime,
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
bool (*getEntriesAfter) (LogStorage self, uint64_t startingTime, uint64_t entryID);
bool (*getEntriesAfter) (LogStorage self, uint64_t startingTime, uint64_t entryID,
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
bool (*getOldestAndNewestEntries) (LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
uint64_t* oldEntry, uint64_t* oldEntryTime);
@ -73,13 +68,12 @@ bool
LogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
bool
LogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime);
LogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
bool
LogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID);
void
LogStorage_setCallbacks(LogStorage self, LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* callbackParameter);
LogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
bool
LogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,

@ -29,6 +29,7 @@
#include "mms_type_spec.h"
#include "mms_common.h"
#include "mms_named_variable_list.h"
#include "logging_api.h"
#ifdef __cplusplus
extern "C" {
@ -52,6 +53,7 @@ typedef struct {
struct sMmsJournal {
char* name;
LogStorage logStorage;
};
typedef struct sMmsJournal* MmsJournal;

@ -1652,8 +1652,10 @@ readJournal(MmsConnection self, MmsError* mmsError, uint32_t invokeId, ByteBuff
if (self->lastResponseError != MMS_ERROR_NONE)
*mmsError = self->lastResponseError;
else if (responseMessage != NULL) {
// if (mmsClient_parseFileOpenResponse(self, &frsmId, fileSize, lastModified) == false)
// *mmsError = MMS_ERROR_PARSING_RESPONSE;
bool moreFollows;
if (mmsClient_parseReadJournalResponse(self, &moreFollows) == false)
*mmsError = MMS_ERROR_PARSING_RESPONSE;
}
releaseResponse(self);

@ -33,6 +33,364 @@
#include "conversions.h"
#include "mms_value_internal.h"
typedef struct sMmsJournalEntry* MmsJournalEntry;
typedef struct sMmsJournalVariable* MmsJournalVariable;
struct sMmsJournalEntry {
MmsValue* entryID; /* type MMS_OCTET_STRING */
MmsValue* occurenceTime; /* type MMS_BINARY_TIME */
LinkedList journalVariables;
};
struct sMmsJournalVariable {
char* tag;
MmsValue* value;
};
//TODO add event-based API to parse journal entries
static bool
parseJournalVariable(uint8_t* buffer, int bufPos, int maxLength, MmsJournalVariable journalVariable)
{
int maxBufPos = bufPos + maxLength;
while (bufPos < maxBufPos) {
int length;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if ((bufPos + length) > maxBufPos) { /* check length field for validity */
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n");
return false;
}
switch (tag) {
case 0x80: /* variableTag */
if (journalVariable->tag == NULL) {
journalVariable->tag = (char*) GLOBAL_MALLOC(length + 1);
memcpy(journalVariable->tag, buffer + bufPos, length);
journalVariable->tag[length] = 0;
printf(" tag: %s\n", journalVariable->tag);
}
break;
case 0xa1: /* valueSpec */
if (journalVariable->value == NULL) {
journalVariable->value = MmsValue_decodeMmsData(buffer, bufPos, length);
}
break;
default:
break;
}
bufPos += length;
}
return true;
}
static bool
parseJournalVariables(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEntry)
{
int maxBufPos = bufPos + maxLength;
while (bufPos < maxBufPos) {
int length;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if ((bufPos + length) > maxBufPos) { /* check length field for validity */
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n");
return false;
}
MmsJournalVariable journalVariable;
switch (tag) {
case 0x30: /* journalVariable */
journalVariable = (MmsJournalVariable)
GLOBAL_CALLOC(1, sizeof(struct sMmsJournalVariable));
parseJournalVariable(buffer, bufPos, length, journalVariable);
LinkedList_add(journalEntry->journalVariables, (void*) journalVariable);
break;
default:
break;
}
bufPos += length;
}
return true;
}
static bool
parseData(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEntry)
{
int maxBufPos = bufPos + maxLength;
while (bufPos < maxBufPos) {
int length;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if ((bufPos + length) > maxBufPos) { /* check length field for validity */
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n");
return false;
}
switch (tag) {
case 0xa1: /* journalVariables */
journalEntry->journalVariables = LinkedList_create();
parseJournalVariables(buffer, bufPos, length, journalEntry);
break;
default:
break;
}
bufPos += length;
}
return true;
}
static bool
parseEntryContent(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEntry)
{
int maxBufPos = bufPos + maxLength;
while (bufPos < maxBufPos) {
int length;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if ((bufPos + length) > maxBufPos) { /* check length field for validity */
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n");
return false;
}
switch (tag) {
case 0x80: /* occurenceTime */
printf(" parse occurenceTime\n");
if (length == 6)
journalEntry->occurenceTime = MmsValue_newBinaryTime(false);
else if (length == 4)
journalEntry->occurenceTime = MmsValue_newBinaryTime(true);
else
break;
memcpy(journalEntry->occurenceTime->value.binaryTime.buf, buffer + bufPos, length);
break;
case 0xa2: /* data */
parseData(buffer, bufPos, length, journalEntry);
break;
default:
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: ignore unknown tag %02x\n", tag);
break;
}
bufPos += length;
}
return true;
}
static bool
parseJournalEntry(uint8_t* buffer, int bufPos, int maxLength, LinkedList journalEntries)
{
int maxBufPos = bufPos + maxLength;
MmsJournalEntry journalEntry = (MmsJournalEntry) GLOBAL_CALLOC(1, sizeof(struct sMmsJournalEntry));
LinkedList_add(journalEntries, (void*) journalEntry);
while (bufPos < maxBufPos) {
int length;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if ((bufPos + length) > maxBufPos) { /* check length field for validity */
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n");
return false;
}
switch (tag) {
case 0x80: /* entryID */
journalEntry->entryID = MmsValue_newOctetString(length, length);
MmsValue_setOctetString(journalEntry->entryID, buffer + bufPos, length);
break;
case 0xa1: /* originatingApplication */
/* ignore */
break;
case 0xa2: /* entryContent */
if (parseEntryContent(buffer, bufPos, length, journalEntry) == false)
return false;
break;
default:
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: unknown tag %02x\n", tag);
return false;
}
bufPos += length;
}
return true;
}
static bool
parseListOfJournalEntries(uint8_t* buffer, int bufPos, int maxLength, LinkedList journalEntries)
{
int maxBufPos = bufPos + maxLength;
while (bufPos < maxBufPos) {
int length;
uint8_t tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if ((bufPos + length) > maxBufPos) { /* check length field for validity */
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n");
return false;
}
switch (tag) {
case 0x30:
printf("Parse journalEntry\n");
if (parseJournalEntry(buffer, bufPos, length, journalEntries) == false)
return false;
break;
default:
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: parseReadJournalResponse: unknown tag %02x\n", tag);
return false;
}
bufPos += length;
}
return true;
}
bool
mmsClient_parseReadJournalResponse(MmsConnection self, bool* moreFollows)
{
uint8_t* buffer = self->lastResponse->buffer;
int maxBufPos = self->lastResponse->size;
int bufPos = self->lastResponseBufPos;
int length;
uint8_t tag = buffer[bufPos++];
if (tag != 0xbf) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: unknown tag %02x\n", tag);
return false;
}
tag = buffer[bufPos++];
*moreFollows = false;
if (tag != 0x41) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: unknown tag %02x\n", tag);
return false;
}
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0)
return false;
int endPos = bufPos + length;
if (endPos > maxBufPos) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: message to short (length:%i maxBufPos:%i)!\n", length, maxBufPos);
return false;
}
LinkedList journalEntries = NULL;
while (bufPos < endPos) {
tag = buffer[bufPos++];
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
switch (tag) {
case 0xa0: /* listOfJournalEntry */
journalEntries = LinkedList_create();
if (!parseListOfJournalEntries(buffer, bufPos, length, journalEntries))
return false;
break;
case 0x81: /* moreFollows */
*moreFollows = BerDecoder_decodeBoolean(buffer, bufPos);
break;
default:
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: message contains unknown tag %02x!\n", tag);
return false;
}
bufPos += length;
}
return true;
}
void
mmsClient_createReadJournalRequest(uint32_t invokeId, ByteBuffer* request, const char* domainId, const char* itemId)
{

@ -76,6 +76,8 @@ MmsDomain_getName(MmsDomain* self)
void
MmsDomain_addJournal(MmsDomain* self, const char* name)
{
printf("CREATE JOURNAL\n");
if (self->journals == NULL)
self->journals = LinkedList_create();
@ -84,6 +86,7 @@ MmsDomain_addJournal(MmsDomain* self, const char* name)
LinkedList_add(self->journals, (void*) journal);
}
MmsJournal
MmsDomain_getJournal(MmsDomain* self, const char* name)
{
@ -93,6 +96,8 @@ MmsDomain_getJournal(MmsDomain* self, const char* name)
MmsJournal mmsJournal = (MmsJournal) LinkedList_getData(journal);
printf(" MMS journal: %s (%s)\n", mmsJournal->name, name);
if (strcmp(mmsJournal->name, name) == 0)
return mmsJournal;

@ -50,19 +50,41 @@ typedef struct {
} JournalEntry;
typedef struct sJournalEncoder* JournalEncoder;
struct sJournalEncoder {
uint8_t* buffer;
int maxSize;
int bufPos;
int currentEntryBufPos; /* store start buffer position of current entry in case the whole JournalEntry will become too long */
uint64_t currentEntryId; //TODO use a byte array for the generic MMS case!
uint64_t currentTimestamp;
bool moreFollows;
};
static bool
entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFollow)
{
if (moreFollow)
printf("Found entry ID:%llu timestamp:%llu\n", entryID, timestamp);
JournalEncoder encoder = (JournalEncoder) parameter;
if (moreFollow) {
//printf("Encode entry ID:%" PRIu64 " timestamp:%" PRIu64 "\n", entryID, timestamp);
if (encoder->moreFollows) {
printf("entryCallback return false\n");
return false;
}
encoder->currentEntryBufPos = encoder->bufPos;
encoder->bufPos += 48; /* reserve space for common entry parts */
encoder->currentEntryId = entryID;
encoder->currentTimestamp = timestamp;
}
else {
printf("Encoded last entry: FINISHED\n");
}
return true;
}
@ -70,46 +92,87 @@ entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFo
static bool
entryDataCallback (void* parameter, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow)
{
JournalEncoder encoder = (JournalEncoder) parameter;
uint8_t* buffer = encoder->buffer;
//TODO check if entry is too long for buffer!
if (moreFollow) {
printf(" EntryData: ref: %s\n", dataRef);
int bufPos = encoder->bufPos;
uint32_t dataRefStrLen = strlen(dataRef);
uint32_t dataRefLen = 1 + BerEncoder_determineLengthSize(dataRefStrLen) + dataRefStrLen;
uint32_t valueSpecLen = 1 + BerEncoder_determineLengthSize(dataSize) + dataSize;
if (bufPos > encoder->maxSize) {
encoder->moreFollows = true;
encoder->bufPos = encoder->currentEntryBufPos; /* remove last entry */
return false;
}
//TODO check if entry is too long for buffer!
MmsValue* value = MmsValue_decodeMmsData(data, 0, dataSize);
bufPos = BerEncoder_encodeTL(0x30, valueSpecLen + dataRefLen, buffer, bufPos);
char buffer[256];
/* encode dataRef */
bufPos = BerEncoder_encodeOctetString(0x80, (uint8_t*) dataRef, dataRefStrLen, buffer, bufPos);
MmsValue_printToBuffer(value, buffer, 256);
/* encode valueSpec */
bufPos = BerEncoder_encodeOctetString(0xa1, data, dataSize, buffer, bufPos);
printf(" value: %s\n", buffer);
//TODO optionally encode reasonCode
encoder->bufPos = bufPos;
}
else {
int dataContentLen = encoder->bufPos - (encoder->currentEntryBufPos + 48);
return true;
}
int journalVariablesLen = 1 + BerEncoder_determineLengthSize(dataContentLen) + dataContentLen;
int dataLen = 1 + BerEncoder_determineLengthSize(journalVariablesLen) + journalVariablesLen;
bool
MmsJournal_queryJournalByRange(MmsJournal self, uint64_t startTime, uint64_t endTime, ByteBuffer* response)
{
// forward request to implementation class
int dataAndTimeLen = dataLen + 8;
//TODO get handle of LogStorage
int entryContentLen = 1 + BerEncoder_determineLengthSize(dataAndTimeLen) + dataAndTimeLen;
struct sJournalEncoder encoderData;
int journalEntryContentLen = 10 /* entryIdentifier */
+ 4 /* originatingApplication */
+ entryContentLen;
LogStorage logStorage;
int headerBufPos = encoder->currentEntryBufPos;
LogStorage_setCallbacks(logStorage, entryCallback, entryDataCallback, &encoderData);
headerBufPos = BerEncoder_encodeTL(0x30, journalEntryContentLen, buffer, headerBufPos);
LogStorage_getEntries(logStorage, startTime, endTime);
headerBufPos = BerEncoder_encodeOctetString(0x80, (uint8_t*) &(encoder->currentEntryId), 8, buffer, headerBufPos);
/* actual encoding will happen in callback handler. When getEntries returns the data is
* already encoded in the buffer.
*/
headerBufPos = BerEncoder_encodeTL(0xa1, 2, buffer, headerBufPos);
headerBufPos = BerEncoder_encodeTL(0x30, 0, buffer, headerBufPos);
// encoder header in response buffer
headerBufPos = BerEncoder_encodeTL(0xa2, dataAndTimeLen, buffer, headerBufPos);
// move encoded JournalEntry data to continue the buffer header
}
MmsValue occurenceTime;
occurenceTime.type = MMS_BINARY_TIME;
occurenceTime.value.binaryTime.size = 6;
MmsValue_setBinaryTime(&occurenceTime, encoder->currentTimestamp);
headerBufPos = BerEncoder_encodeOctetString(0x80, occurenceTime.value.binaryTime.buf, 6, buffer, headerBufPos);
headerBufPos = BerEncoder_encodeTL(0xa2, journalVariablesLen, buffer, headerBufPos);
headerBufPos = BerEncoder_encodeTL(0xa1, dataContentLen, buffer, headerBufPos);
int entryHeaderLength = headerBufPos - encoder->currentEntryBufPos;
/* move data to entry header */
memmove(buffer + (encoder->currentEntryBufPos + entryHeaderLength), buffer + (encoder->currentEntryBufPos + 48),
dataContentLen);
/* prepare buffer for next entry. */
encoder->bufPos = encoder->currentEntryBufPos + entryHeaderLength + dataContentLen;
}
return true;
}
static bool
parseStringWithMaxLength(char* filename, int maxLength, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response)
@ -141,6 +204,7 @@ parseStringWithMaxLength(char* filename, int maxLength, uint8_t* buffer, int* bu
return true;
}
#define RESERVED_SPACE_FOR_HEADER 22
void
mmsServer_handleReadJournalRequest(
@ -154,7 +218,7 @@ mmsServer_handleReadJournalRequest(
char domainId[65];
char logName[65];
char entryIdBuf[64]; /* maximum size of entry id is 64 bytes! */
uint8_t entryIdBuf[64]; /* maximum size of entry id is 64 bytes! */
MmsValue entrySpec;
entrySpec.type = MMS_OCTET_STRING;
@ -243,7 +307,7 @@ mmsServer_handleReadJournalRequest(
}
else {
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return;
return; // forward request to implementation class
}
bufPos += length;
@ -277,7 +341,7 @@ mmsServer_handleReadJournalRequest(
}
else {
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return;
return; // forward request to implementation class
}
bufPos += length;
@ -352,11 +416,15 @@ mmsServer_handleReadJournalRequest(
}
//TODO check if required fields are present
if (hasNames == false) {
printf("MMS_SERVER: readJournal missing journal name\n");
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return;
}
//TODO check valid field combinations
/* lookup journal */
MmsServer server = connection->server;
MmsDevice* mmsDevice = MmsServer_getDevice(connection->server);
MmsDomain* mmsDomain = MmsDevice_getDomain(mmsDevice, domainId);
@ -376,6 +444,86 @@ mmsServer_handleReadJournalRequest(
}
printf("Read journal %s ...\n", mmsJournal->name);
struct sJournalEncoder encoder;
encoder.buffer = response->buffer;
encoder.moreFollows = false;
encoder.maxSize = connection->maxPduSize - 3; /* reserve three bytes for moreFollows */
encoder.bufPos = RESERVED_SPACE_FOR_HEADER; /* reserve space for header */
LogStorage logStorage = mmsJournal->logStorage;
if (logStorage != NULL) {
if (hasRangeStartSpec && hasRangeStopSpec) {
LogStorage_getEntries(logStorage, MmsValue_getBinaryTimeAsUtcMs(&rangeStart), MmsValue_getBinaryTimeAsUtcMs(&rangeStop),
entryCallback, entryDataCallback, &encoder);
}
else if (hasEntrySpec && hasTimeSpec) {
LogStorage_getEntriesAfter(logStorage, MmsValue_getBinaryTimeAsUtcMs(&rangeStart), *((uint64_t*) entryIdBuf),
entryCallback, entryDataCallback, &encoder);
}
else {
printf("MMS_SERVER: readJournal missing valid argument combination\n");
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return;
}
}
/* actual encoding will happen in callback handler. When getEntries returns the data is
* already encoded in the buffer.
*/
/* encode message header in response buffer */
uint8_t* buffer = encoder.buffer;
bufPos = 0;
int dataSize = encoder.bufPos - RESERVED_SPACE_FOR_HEADER;
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2;
uint32_t listOfEntriesLen = 1 + BerEncoder_determineLengthSize(dataSize) + dataSize;
uint32_t moreFollowsLen;
if (encoder.moreFollows)
moreFollowsLen = 3;
else
moreFollowsLen = 0;
// uint32_t readJournalLen = 2 + BerEncoder_determineLengthSize(listOfEntriesLen + moreFollowsLen) +
// (listOfEntriesLen + moreFollowsLen);
uint32_t readJournalLen = 2 + BerEncoder_determineLengthSize(listOfEntriesLen + moreFollowsLen) +
(listOfEntriesLen + moreFollowsLen);
uint32_t confirmedResponsePDUSize = readJournalLen + invokeIdSize;
bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32((uint32_t) invokeId, buffer, bufPos);
buffer[bufPos++] = 0xbf;
buffer[bufPos++] = 0x41;
bufPos = BerEncoder_encodeLength(listOfEntriesLen + moreFollowsLen, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0xa0, dataSize, buffer, bufPos);
/* move encoded JournalEntry data to continue the buffer header */
printf("Encoded message header with %i bytes\n", bufPos);
memmove(buffer + bufPos, buffer + RESERVED_SPACE_FOR_HEADER, dataSize);
bufPos = bufPos + dataSize;
/* encode morefollows */
if (encoder.moreFollows)
bufPos = BerEncoder_encodeBoolean(0x81, true, buffer, bufPos);
response->size = bufPos;
}
#endif /* (MMS_JOURNAL_SERVICE == 1) */

Loading…
Cancel
Save