Merge branch 'logging'
commit
9c8da7f94c
@ -0,0 +1,331 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
|
||||
<Services>
|
||||
<DynAssociation max="5"/>
|
||||
<GetDirectory />
|
||||
<DataObjectDirectory />
|
||||
<GetDataObjectDefinition />
|
||||
<GetDataSetValue />
|
||||
<DataSetDirectory />
|
||||
<ReadWrite />
|
||||
<GetCBValues />
|
||||
<ConfDataSet max="1" modify="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>
|
||||
</Services>
|
||||
|
||||
<AccessPoint name="accessPoint1">
|
||||
<Server>
|
||||
<Authentication />
|
||||
<LDevice inst="GenericIO">
|
||||
|
||||
<LN0 lnClass="LLN0" lnType="LLN01" inst="">
|
||||
|
||||
<DataSet name="Events" desc="Events">
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO1" daName="stVal" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO2" daName="stVal" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="SPCSO3" daName="stVal" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1" doName="DPCSO1" daName="stVal" />
|
||||
</DataSet>
|
||||
|
||||
<ReportControl name="EventsRCB" confRev="1" datSet="Events" rptID="Events1" buffered="false" intgPd="1000" bufTime="50">
|
||||
<TrgOps period="true" />
|
||||
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" entryID="true" configRef="true" />
|
||||
<RptEnabled max="5" />
|
||||
</ReportControl>
|
||||
|
||||
</LN0>
|
||||
|
||||
<LN lnClass="LPHD" lnType="LPHD1" inst="1" prefix="" />
|
||||
|
||||
<LN lnClass="GGIO" lnType="GGIO1" inst="1" prefix="">
|
||||
|
||||
<DOI name="SPCSO1">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
<DOI name="SPCSO2">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
<DOI name="SPCSO3">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
<DOI name="DPCSO1">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
</LN>
|
||||
|
||||
<LN lnClass="GAPC" lnType="TIM_GAPC" inst="1" prefix="TIM_" />
|
||||
|
||||
</LDevice>
|
||||
</Server>
|
||||
</AccessPoint>
|
||||
</IED>
|
||||
|
||||
<DataTypeTemplates>
|
||||
<LNodeType id="LLN01" lnClass="LLN0">
|
||||
<DO name="Beh" type="ENS_Beh" />
|
||||
<DO name="Mod" type="ENC_Mod_status_only" />
|
||||
<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_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_Health" />
|
||||
<DO name="NamPlt" type="LPL_2_NamPlt" />
|
||||
<DO name="AnIn1" type="MV_1_AnIn1" />
|
||||
<DO name="AnIn2" type="MV_1_AnIn1" />
|
||||
<DO name="AnIn3" type="MV_1_AnIn1" />
|
||||
<DO name="AnIn4" type="MV_1_AnIn1" />
|
||||
<DO name="SPCSO1" type="SPC_2_SPCSO1" />
|
||||
<DO name="SPCSO2" type="SPC_1_SPCSO2" />
|
||||
<DO name="SPCSO3" type="SPC_1_SPCSO3" />
|
||||
<DO name="DPCSO1" type="DPC_1_DPCSO1" />
|
||||
<DO name="Ind1" type="SPS_1_Proxy" />
|
||||
<DO name="Ind2" type="SPS_1_Proxy" />
|
||||
<DO name="Ind3" type="SPS_1_Proxy" />
|
||||
<DO name="Ind4" type="SPS_1_Proxy" />
|
||||
</LNodeType>
|
||||
|
||||
<LNodeType id="TIM_GAPC" lnClass="GAPC">
|
||||
<DO name="Beh" type="ENS_Beh" />
|
||||
<DO name="Mod" type="ENC_Mod_status_only" />
|
||||
|
||||
<DO name="Str" type="ACD_Str" />
|
||||
<DO name="Op" type="ACT_Op" />
|
||||
|
||||
<DO name="OpDlTmms" type="ING_EXT" />
|
||||
<DO name="RsDlTmms" type="ING_EXT" />
|
||||
|
||||
<DO name="OpCntRs" type="INC_OpCntRs" />
|
||||
</LNodeType>
|
||||
|
||||
<DOType id="ING_EXT" cdc="ING">
|
||||
<DA name="setVal" bType="INT32" fc="SP" dchg="true" />
|
||||
<DA name="dataNs" bType="VisString255" fc="EX">
|
||||
<Val>EXT:2015</Val>
|
||||
</DA>
|
||||
</DOType>
|
||||
|
||||
<DOType id="ENS_Beh" cdc="ENS">
|
||||
<DA name="stVal" fc="ST" bType="Enum" type="Beh" dchg="true" />
|
||||
<DA name="q" fc="ST" bType="Quality" qchg="true" />
|
||||
<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="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" />
|
||||
<DA bType="Quality" fc="ST" name="q" qchg="true" />
|
||||
<DA bType="Timestamp" fc="ST" name="t" />
|
||||
</DOType>
|
||||
|
||||
<DOType cdc="ACT" id="ACT_Op">
|
||||
<DA bType="BOOLEAN" dchg="true" fc="ST" name="general"/>
|
||||
<DA bType="Quality" fc="ST" name="q" qchg="true"/>
|
||||
<DA bType="Timestamp" fc="ST" name="t"/>
|
||||
</DOType>
|
||||
|
||||
<DOType cdc="ENC" id="ENC_Mod_status_only">
|
||||
<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" valKind="RO" type="CtlModels">
|
||||
<Val>status-only</Val>
|
||||
</DA>
|
||||
</DOType>
|
||||
|
||||
<DOType id="LPL_1_NamPlt" cdc="LPL">
|
||||
<DA name="vendor" bType="VisString255" fc="DC" />
|
||||
<DA name="swRev" bType="VisString255" fc="DC" />
|
||||
<DA name="d" bType="VisString255" fc="DC" />
|
||||
<DA name="configRev" bType="VisString255" fc="DC" />
|
||||
<DA name="ldNs" bType="VisString255" fc="EX" />
|
||||
</DOType>
|
||||
|
||||
<DOType id="DPL_1_PhyNam" cdc="DPL">
|
||||
<DA name="vendor" bType="VisString255" fc="DC" />
|
||||
</DOType>
|
||||
|
||||
<DOType id="SPS_1_Proxy" cdc="SPS">
|
||||
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
|
||||
<DA name="q" bType="Quality" fc="ST" qchg="true" />
|
||||
<DA name="t" bType="Timestamp" fc="ST" />
|
||||
</DOType>
|
||||
|
||||
<DOType id="LPL_2_NamPlt" cdc="LPL">
|
||||
<DA name="vendor" bType="VisString255" fc="DC" />
|
||||
<DA name="swRev" bType="VisString255" fc="DC" />
|
||||
<DA name="d" bType="VisString255" fc="DC" />
|
||||
</DOType>
|
||||
|
||||
<DOType id="MV_1_AnIn1" cdc="MV">
|
||||
<DA name="mag" type="AnalogueValue_1" bType="Struct" fc="MX" dchg="true" />
|
||||
<DA name="q" bType="Quality" fc="MX" qchg="true" />
|
||||
<DA name="t" bType="Timestamp" fc="MX" />
|
||||
</DOType>
|
||||
|
||||
<DOType id="DPC_1_DPCSO1" cdc="DPC">
|
||||
<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" valKind="RO" bType="Enum" fc="CF" />
|
||||
<DA name="t" bType="Timestamp" fc="ST" />
|
||||
</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" 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="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" valKind="RO" bType="Enum" fc="CF" />
|
||||
<DA name="t" bType="Timestamp" fc="ST" />
|
||||
</DOType>
|
||||
|
||||
<DOType id="SPC_1_SPCSO3" 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" valKind="RO" bType="Enum" fc="CF" />
|
||||
<DA name="t" bType="Timestamp" fc="ST" />
|
||||
</DOType>
|
||||
|
||||
<DAType id="AnalogueValue_1">
|
||||
<BDA name="f" bType="FLOAT32" />
|
||||
</DAType>
|
||||
|
||||
<DAType id="Originator_1">
|
||||
<BDA name="orCat" type="OrCat" bType="Enum" />
|
||||
<BDA name="orIdent" bType="Octet64" />
|
||||
</DAType>
|
||||
|
||||
<DAType id="SPCOperate_1">
|
||||
<BDA name="ctlVal" bType="BOOLEAN" />
|
||||
<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="SPCCancel">
|
||||
<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>
|
||||
<EnumVal ord="2">blocked</EnumVal>
|
||||
<EnumVal ord="3">test</EnumVal>
|
||||
<EnumVal ord="4">test/blocked</EnumVal>
|
||||
<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>
|
||||
<EnumVal ord="2">backward</EnumVal>
|
||||
<EnumVal ord="3">both</EnumVal>
|
||||
</EnumType>
|
||||
|
||||
<EnumType id="CtlModels">
|
||||
<EnumVal ord="0">status-only</EnumVal>
|
||||
<EnumVal ord="1">direct-with-normal-security</EnumVal>
|
||||
</EnumType>
|
||||
|
||||
<EnumType id="OrCat">
|
||||
<EnumVal ord="0">not-supported</EnumVal>
|
||||
<EnumVal ord="1">bay-control</EnumVal>
|
||||
<EnumVal ord="2">station-control</EnumVal>
|
||||
<EnumVal ord="3">remote-control</EnumVal>
|
||||
<EnumVal ord="4">automatic-bay</EnumVal>
|
||||
<EnumVal ord="5">automatic-station</EnumVal>
|
||||
<EnumVal ord="6">automatic-remote</EnumVal>
|
||||
<EnumVal ord="7">maintenance</EnumVal>
|
||||
<EnumVal ord="8">process</EnumVal>
|
||||
</EnumType>
|
||||
</DataTypeTemplates>
|
||||
</SCL>
|
||||
@ -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
|
||||
)
|
||||
@ -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)
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/sqlite/sqlite3.h")
|
||||
message("Found sqlite source code -> compile sqlite-log driver with static sqlite library")
|
||||
|
||||
include_directories(
|
||||
.
|
||||
${CMAKE_SOURCE_DIR}/third_party/sqlite
|
||||
)
|
||||
|
||||
set(server_example_SRCS
|
||||
server_example_logging.c
|
||||
static_model.c
|
||||
${CMAKE_SOURCE_DIR}/src/logging/drivers/sqlite/log_storage_sqlite.c
|
||||
)
|
||||
|
||||
set(sqlite_SRCS
|
||||
${CMAKE_SOURCE_DIR}/third_party/sqlite/sqlite3.c
|
||||
)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION")
|
||||
|
||||
IF(WIN32)
|
||||
set_source_files_properties(${server_example_SRCS}
|
||||
PROPERTIES LANGUAGE CXX)
|
||||
ENDIF(WIN32)
|
||||
|
||||
add_executable(server_example_logging
|
||||
${server_example_SRCS}
|
||||
${sqlite_SRCS}
|
||||
)
|
||||
|
||||
target_link_libraries(server_example_logging
|
||||
iec61850
|
||||
)
|
||||
|
||||
ELSE()
|
||||
|
||||
message("server-example-logging: sqlite not found")
|
||||
|
||||
ENDIF()
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
LIBIEC_HOME=../..
|
||||
|
||||
PROJECT_BINARY_NAME = server_example_logging
|
||||
PROJECT_SOURCES = server_example_logging.c
|
||||
PROJECT_SOURCES += static_model.c
|
||||
PROJECT_SOURCES += $(LIBIEC_HOME)/src/logging/drivers/sqlite/log_storage_sqlite.c
|
||||
|
||||
PROJECT_ICD_FILE = simpleIO_direct_control.icd
|
||||
|
||||
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
|
||||
|
||||
LDLIBS += -lm -lsqlite3
|
||||
|
||||
CP = cp
|
||||
|
||||
model: $(PROJECT_ICD_FILE)
|
||||
java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE)
|
||||
|
||||
$(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)
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
LIBIEC_HOME=../..
|
||||
|
||||
PROJECT_BINARY_NAME = server_example_logging
|
||||
PROJECT_SOURCES = server_example_logging.c
|
||||
PROJECT_SOURCES += static_model.c
|
||||
PROJECT_SOURCES += $(LIBIEC_HOME)/src/logging/drivers/sqlite/log_storage_sqlite.c
|
||||
PROJECT_SOURCES += $(LIBIEC_HOME)/third_party/sqlite/sqlite3.c
|
||||
|
||||
PROJECT_ICD_FILE = simpleIO_direct_control.icd
|
||||
|
||||
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
|
||||
|
||||
CFLAGS += -I$(LIBIEC_HOME)/third_party/sqlite
|
||||
CFLAGS += -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DHAVE_USLEEP
|
||||
|
||||
LDLIBS += -lm
|
||||
|
||||
CP = cp
|
||||
|
||||
model: $(PROJECT_ICD_FILE)
|
||||
java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE)
|
||||
|
||||
$(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)
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
BUILD THE EXAMPLE:
|
||||
|
||||
To build the logging example it is required to have sqlite present!
|
||||
|
||||
If you have sqlite installed on the system (including the header files) e.g. in an Ubuntu installation with the sqlite3 package installed, you can simply use the Makefile.
|
||||
|
||||
If you don't have sqlite installed you have to download the sqlite amalgamation package and install the files sqlite3.c, sqlite3.h in the libiec61850/third_party/sqlite folder and use the Makefile.sqliteStatic instead. This will build a version of the example with the sqlite code statically linked.
|
||||
|
||||
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* server_example_logging.c
|
||||
*
|
||||
* - How to use a server with logging service
|
||||
* - How to store arbitrary data in a log
|
||||
*
|
||||
*/
|
||||
|
||||
#include "iec61850_server.h"
|
||||
#include "hal_thread.h"
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "static_model.h"
|
||||
|
||||
#include "logging_api.h"
|
||||
|
||||
LogStorage SqliteLogStorage_createInstance(const char*);
|
||||
|
||||
/* import IEC 61850 device model created from SCL-File */
|
||||
extern IedModel iedModel;
|
||||
|
||||
static int running = 0;
|
||||
static IedServer iedServer = NULL;
|
||||
|
||||
void
|
||||
sigint_handler(int signalId)
|
||||
{
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static ControlHandlerResult
|
||||
controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test)
|
||||
{
|
||||
if (test)
|
||||
return CONTROL_RESULT_FAILED;
|
||||
|
||||
if (MmsValue_getType(value) == MMS_BOOLEAN) {
|
||||
printf("received binary control command: ");
|
||||
|
||||
if (MmsValue_getBoolean(value))
|
||||
printf("on\n");
|
||||
else
|
||||
printf("off\n");
|
||||
}
|
||||
else
|
||||
return CONTROL_RESULT_FAILED;
|
||||
|
||||
uint64_t timeStamp = Hal_getTimeInMs();
|
||||
|
||||
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) {
|
||||
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp);
|
||||
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);
|
||||
}
|
||||
|
||||
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2) {
|
||||
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp);
|
||||
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value);
|
||||
}
|
||||
|
||||
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) {
|
||||
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp);
|
||||
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value);
|
||||
}
|
||||
|
||||
if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4) {
|
||||
IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, timeStamp);
|
||||
IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value);
|
||||
}
|
||||
|
||||
return CONTROL_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter)
|
||||
{
|
||||
if (connected)
|
||||
printf("Connection opened\n");
|
||||
else
|
||||
printf("Connection closed\n");
|
||||
}
|
||||
|
||||
static bool
|
||||
entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFollow)
|
||||
{
|
||||
#if 0
|
||||
if (moreFollow)
|
||||
printf("Found entry ID:%llu timestamp:%llu\n", entryID, timestamp);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow)
|
||||
{
|
||||
#if 0
|
||||
if (moreFollow) {
|
||||
printf(" EntryData: ref: %s\n", dataRef);
|
||||
|
||||
MmsValue* value = MmsValue_decodeMmsData(data, 0, dataSize);
|
||||
|
||||
char buffer[256];
|
||||
|
||||
MmsValue_printToBuffer(value, buffer, 256);
|
||||
|
||||
printf(" value: %s\n", buffer);
|
||||
|
||||
MmsValue_delete(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
|
||||
|
||||
iedServer = IedServer_create(&iedModel);
|
||||
|
||||
/* Install handler for operate command */
|
||||
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,
|
||||
(ControlHandler) controlHandlerForBinaryOutput,
|
||||
IEDMODEL_GenericIO_GGIO1_SPCSO1);
|
||||
|
||||
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2,
|
||||
(ControlHandler) controlHandlerForBinaryOutput,
|
||||
IEDMODEL_GenericIO_GGIO1_SPCSO2);
|
||||
|
||||
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3,
|
||||
(ControlHandler) controlHandlerForBinaryOutput,
|
||||
IEDMODEL_GenericIO_GGIO1_SPCSO3);
|
||||
|
||||
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4,
|
||||
(ControlHandler) controlHandlerForBinaryOutput,
|
||||
IEDMODEL_GenericIO_GGIO1_SPCSO4);
|
||||
|
||||
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
|
||||
|
||||
LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db");
|
||||
|
||||
IedServer_setLogStorage(iedServer, "GenericIO/LLN0$EventLog", statusLog);
|
||||
|
||||
uint64_t entryID = LogStorage_addEntry(statusLog, Hal_getTimeInMs());
|
||||
|
||||
MmsValue* value = MmsValue_newIntegerFromInt32(123);
|
||||
|
||||
uint8_t blob[256];
|
||||
|
||||
int blobSize = MmsValue_encodeMmsData(value, blob, 0, true);
|
||||
|
||||
|
||||
LogStorage_addEntryData(statusLog, entryID, "simpleIOGenerioIO/GPIO1$ST$SPCSO1$stVal", blob, blobSize, 0);
|
||||
|
||||
MmsValue_delete(value);
|
||||
|
||||
value = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
|
||||
|
||||
blobSize = MmsValue_encodeMmsData(value, blob, 0, true);
|
||||
|
||||
MmsValue_delete(value);
|
||||
|
||||
LogStorage_addEntryData(statusLog, entryID, "simpleIOGenerioIO/GPIO1$ST$SPCSO1$t", blob, blobSize, 0);
|
||||
|
||||
LogStorage_getEntries(statusLog, 0, Hal_getTimeInMs(), entryCallback, (LogEntryDataCallback) entryDataCallback, NULL);
|
||||
|
||||
/* MMS server will be instructed to start listening to client connections. */
|
||||
IedServer_start(iedServer, 102);
|
||||
|
||||
if (!IedServer_isRunning(iedServer)) {
|
||||
printf("Starting server failed! Exit.\n");
|
||||
IedServer_destroy(iedServer);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
running = 1;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
float t = 0.f;
|
||||
|
||||
while (running) {
|
||||
uint64_t timestamp = Hal_getTimeInMs();
|
||||
|
||||
t += 0.1f;
|
||||
|
||||
float an1 = sinf(t);
|
||||
float an2 = sinf(t + 1.f);
|
||||
float an3 = sinf(t + 2.f);
|
||||
float an4 = sinf(t + 3.f);
|
||||
|
||||
IedServer_lockDataModel(iedServer);
|
||||
|
||||
Timestamp iecTimestamp;
|
||||
|
||||
Timestamp_clearFlags(&iecTimestamp);
|
||||
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
|
||||
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
|
||||
|
||||
/* toggle clock-not-synchronized flag in timestamp */
|
||||
if (((int) t % 2) == 0)
|
||||
Timestamp_setClockNotSynchronized(&iecTimestamp, true);
|
||||
|
||||
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp);
|
||||
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1);
|
||||
|
||||
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, &iecTimestamp);
|
||||
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, an2);
|
||||
|
||||
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, &iecTimestamp);
|
||||
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, an3);
|
||||
|
||||
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, &iecTimestamp);
|
||||
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, an4);
|
||||
|
||||
IedServer_unlockDataModel(iedServer);
|
||||
|
||||
Thread_sleep(100);
|
||||
}
|
||||
|
||||
/* stop MMS server - close TCP server socket and all client sockets */
|
||||
IedServer_stop(iedServer);
|
||||
|
||||
/* Cleanup - free all resources */
|
||||
IedServer_destroy(iedServer);
|
||||
|
||||
/* Release connection to database and free resources */
|
||||
LogStorage_destroy(statusLog);
|
||||
|
||||
} /* main() */
|
||||
@ -0,0 +1,306 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SCL xmlns="http://www.iec.ch/61850/2003/SCL">
|
||||
<Header id="" nameStructure="IEDName">
|
||||
</Header>
|
||||
<Communication>
|
||||
<SubNetwork name="subnetwork1" type="8-MMS">
|
||||
<Text>Station bus</Text>
|
||||
<BitRate unit="b/s">10</BitRate>
|
||||
<ConnectedAP iedName="simpleIO" 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="simpleIO">
|
||||
<Services>
|
||||
<DynAssociation />
|
||||
<GetDirectory />
|
||||
<GetDataObjectDefinition />
|
||||
<GetDataSetValue />
|
||||
<DataSetDirectory />
|
||||
<ReadWrite />
|
||||
<GetCBValues />
|
||||
<ConfLNs fixPrefix="true" fixLnInst="true" />
|
||||
<GOOSE max="5" />
|
||||
<GSSE max="5" />
|
||||
<FileHandling />
|
||||
<GSEDir />
|
||||
<TimerActivatedControl />
|
||||
</Services>
|
||||
<AccessPoint name="accessPoint1">
|
||||
<Server>
|
||||
<Authentication />
|
||||
<LDevice inst="GenericIO">
|
||||
<LN0 lnClass="LLN0" lnType="LLN01" inst="">
|
||||
|
||||
<DataSet name="Events" desc="Events">
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO1" daName="stVal" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO2" daName="stVal" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO3" daName="stVal" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO4" daName="stVal" />
|
||||
</DataSet>
|
||||
|
||||
<DataSet name="Events2" desc="Events2">
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO1" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO2" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO3" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="ST" lnInst="1"
|
||||
doName="SPCSO4" />
|
||||
</DataSet>
|
||||
|
||||
<DataSet name="Measurements" desc="Measurements">
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn1" daName="mag.f" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn1" daName="q" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn2" daName="mag.f" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn2" daName="q" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn3" daName="mag.f" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn3" daName="q" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn4" daName="mag.f" />
|
||||
<FCDA ldInst="GenericIO" lnClass="GGIO" fc="MX" lnInst="1"
|
||||
doName="AnIn4" daName="q" />
|
||||
</DataSet>
|
||||
|
||||
<ReportControl name="EventsRCB" confRev="4294967295"
|
||||
datSet="Events" rptID="Events1" buffered="false" intgPd="1000"
|
||||
bufTime="50">
|
||||
<TrgOps period="true" />
|
||||
<OptFields seqNum="true" timeStamp="true" dataSet="true"
|
||||
reasonCode="true" entryID="true" configRef="true" />
|
||||
<RptEnabled max="1" />
|
||||
</ReportControl>
|
||||
|
||||
<ReportControl name="EventsIndexed" indexed="true"
|
||||
confRev="1" datSet="Events" rptID="Events2" buffered="false"
|
||||
intgPd="1000" bufTime="50">
|
||||
<TrgOps period="true" />
|
||||
<OptFields seqNum="true" timeStamp="true" dataSet="true"
|
||||
reasonCode="true" entryID="true" configRef="true" />
|
||||
<RptEnabled max="3" />
|
||||
</ReportControl>
|
||||
|
||||
<ReportControl name="Measurements" indexed="true"
|
||||
confRev="1" datSet="Measurements" rptID="Measurements" buffered="true"
|
||||
intgPd="1000" bufTime="50">
|
||||
<TrgOps period="false" />
|
||||
<OptFields seqNum="true" timeStamp="true" dataSet="true"
|
||||
reasonCode="true" entryID="true" configRef="true" />
|
||||
<RptEnabled max="3" />
|
||||
</ReportControl>
|
||||
|
||||
<LogControl name="EventLog" datSet="Events" logName="EventLog">
|
||||
<TrgOps dchg="true" qchg="true" />
|
||||
</LogControl>
|
||||
|
||||
<LogControl name="GeneralLog" datSet="" logName="">
|
||||
<TrgOps dchg="true" qchg="true" />
|
||||
</LogControl>
|
||||
|
||||
|
||||
<Log />
|
||||
<Log name="EventLog" />
|
||||
|
||||
<DOI name="Mod">
|
||||
<DAI name="ctlModel">
|
||||
<Val>status-only</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
|
||||
<DOI name="NamPlt">
|
||||
<DAI name="vendor">
|
||||
<Val>MZ Automation</Val>
|
||||
</DAI>
|
||||
<DAI name="swRev">
|
||||
<Val>0.7.3</Val>
|
||||
</DAI>
|
||||
<DAI name="d">
|
||||
<Val>libiec61850 server example</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>
|
||||
</DAI>
|
||||
</DOI>
|
||||
<DOI name="SPCSO2">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
<DOI name="SPCSO3">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
<DOI name="SPCSO4">
|
||||
<DAI name="ctlModel">
|
||||
<Val>direct-with-normal-security</Val>
|
||||
</DAI>
|
||||
</DOI>
|
||||
</LN>
|
||||
</LDevice>
|
||||
</Server>
|
||||
</AccessPoint>
|
||||
</IED>
|
||||
<DataTypeTemplates>
|
||||
<LNodeType id="LLN01" lnClass="LLN0">
|
||||
<DO name="Mod" type="INC_1_Mod" />
|
||||
<DO name="Beh" type="INS_1_Beh" />
|
||||
<DO name="Health" type="INS_1_Beh" />
|
||||
<DO name="NamPlt" type="LPL_1_NamPlt" />
|
||||
</LNodeType>
|
||||
<LNodeType id="LPHD1" lnClass="LPHD">
|
||||
<DO name="PhyNam" type="DPL_1_PhyNam" />
|
||||
<DO name="PhyHealth" type="INS_1_Beh" />
|
||||
<DO name="Proxy" type="SPS_1_Proxy" />
|
||||
</LNodeType>
|
||||
<LNodeType id="GGIO1" lnClass="GGIO">
|
||||
<DO name="Mod" type="INC_2_Mod" />
|
||||
<DO name="Beh" type="INS_1_Beh" />
|
||||
<DO name="Health" type="INS_1_Beh" />
|
||||
<DO name="NamPlt" type="LPL_2_NamPlt" />
|
||||
<DO name="AnIn1" type="MV_1_AnIn1" />
|
||||
<DO name="AnIn2" type="MV_1_AnIn1" />
|
||||
<DO name="AnIn3" type="MV_1_AnIn1" />
|
||||
<DO name="AnIn4" type="MV_1_AnIn1" />
|
||||
<DO name="SPCSO1" type="SPC_2_SPCSO1" />
|
||||
<DO name="SPCSO2" type="SPC_1_SPCSO2" />
|
||||
<DO name="SPCSO3" type="SPC_1_SPCSO3" />
|
||||
<DO name="SPCSO4" type="SPC_1_SPCSO1" />
|
||||
<DO name="Ind1" type="SPS_1_Proxy" />
|
||||
<DO name="Ind2" type="SPS_1_Proxy" />
|
||||
<DO name="Ind3" type="SPS_1_Proxy" />
|
||||
<DO name="Ind4" type="SPS_1_Proxy" />
|
||||
</LNodeType>
|
||||
<DOType id="INC_1_Mod" 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="ctlModel" type="CtlModels" bType="Enum" fc="CF" />
|
||||
</DOType>
|
||||
<DOType id="INS_1_Beh" cdc="INS">
|
||||
<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" />
|
||||
</DOType>
|
||||
<DOType id="LPL_1_NamPlt" cdc="LPL">
|
||||
<DA name="vendor" bType="VisString255" fc="DC" />
|
||||
<DA name="swRev" bType="VisString255" fc="DC" />
|
||||
<DA name="d" bType="VisString255" fc="DC" />
|
||||
<DA name="configRev" bType="VisString255" fc="DC" />
|
||||
<DA name="ldNs" bType="VisString255" fc="EX" />
|
||||
</DOType>
|
||||
<DOType id="DPL_1_PhyNam" cdc="DPL">
|
||||
<DA name="vendor" bType="VisString255" fc="DC" />
|
||||
</DOType>
|
||||
<DOType id="SPS_1_Proxy" cdc="SPS">
|
||||
<DA name="stVal" bType="BOOLEAN" fc="ST" dchg="true" />
|
||||
<DA name="q" bType="Quality" fc="ST" qchg="true" />
|
||||
<DA name="t" bType="Timestamp" fc="ST" />
|
||||
</DOType>
|
||||
<DOType id="LPL_2_NamPlt" cdc="LPL">
|
||||
<DA name="vendor" bType="VisString255" fc="DC" />
|
||||
<DA name="swRev" bType="VisString255" fc="DC" />
|
||||
<DA name="d" bType="VisString255" fc="DC" />
|
||||
</DOType>
|
||||
<DOType id="MV_1_AnIn1" cdc="MV">
|
||||
<DA name="mag" type="AnalogueValue_1" bType="Struct" fc="MX" dchg="true" />
|
||||
<DA name="q" bType="Quality" fc="MX" qchg="true" />
|
||||
<DA name="t" bType="Timestamp" fc="MX" />
|
||||
</DOType>
|
||||
<DOType id="SPC_1_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="t" bType="Timestamp" fc="ST" />
|
||||
</DOType>
|
||||
<DOType id="INC_2_Mod" cdc="INC">
|
||||
<DA name="q" bType="Quality" fc="ST" qchg="true" />
|
||||
<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="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="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="SPC_1_SPCSO3" 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="t" bType="Timestamp" fc="ST" />
|
||||
</DOType>
|
||||
<DAType id="AnalogueValue_1">
|
||||
<BDA name="f" bType="FLOAT32" />
|
||||
</DAType>
|
||||
<DAType id="Originator_1">
|
||||
<BDA name="orCat" type="OrCat" bType="Enum" />
|
||||
<BDA name="orIdent" bType="Octet64" />
|
||||
</DAType>
|
||||
<DAType id="SPCOperate_1">
|
||||
<BDA name="ctlVal" bType="BOOLEAN" />
|
||||
<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="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">
|
||||
<EnumVal ord="0">not-supported</EnumVal>
|
||||
<EnumVal ord="1">bay-control</EnumVal>
|
||||
<EnumVal ord="2">station-control</EnumVal>
|
||||
<EnumVal ord="3">remote-control</EnumVal>
|
||||
<EnumVal ord="4">automatic-bay</EnumVal>
|
||||
<EnumVal ord="5">automatic-station</EnumVal>
|
||||
<EnumVal ord="6">automatic-remote</EnumVal>
|
||||
<EnumVal ord="7">maintenance</EnumVal>
|
||||
<EnumVal ord="8">process</EnumVal>
|
||||
</EnumType>
|
||||
</DataTypeTemplates>
|
||||
</SCL>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* static_model.h
|
||||
*
|
||||
* automatically generated from simpleIO_direct_control.icd
|
||||
*/
|
||||
|
||||
#ifndef STATIC_MODEL_H_
|
||||
#define STATIC_MODEL_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "iec61850_model.h"
|
||||
|
||||
extern IedModel iedModel;
|
||||
extern LogicalDevice iedModel_GenericIO;
|
||||
extern LogicalNode iedModel_GenericIO_LLN0;
|
||||
extern DataObject iedModel_GenericIO_LLN0_Mod;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Mod_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Mod_q;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Mod_t;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel;
|
||||
extern DataObject iedModel_GenericIO_LLN0_Beh;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Beh_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Beh_q;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Beh_t;
|
||||
extern DataObject iedModel_GenericIO_LLN0_Health;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Health_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Health_q;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_Health_t;
|
||||
extern DataObject iedModel_GenericIO_LLN0_NamPlt;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_d;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev;
|
||||
extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs;
|
||||
extern LogicalNode iedModel_GenericIO_LPHD1;
|
||||
extern DataObject iedModel_GenericIO_LPHD1_PhyNam;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor;
|
||||
extern DataObject iedModel_GenericIO_LPHD1_PhyHealth;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t;
|
||||
extern DataObject iedModel_GenericIO_LPHD1_Proxy;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_q;
|
||||
extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_t;
|
||||
extern LogicalNode iedModel_GenericIO_GGIO1;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Mod;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Beh;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Beh_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Beh_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Health;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Health_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Health_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Health_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_NamPlt;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_AnIn1;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_AnIn2;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_AnIn3;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_AnIn4;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_SPCSO1;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_SPCSO2;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_SPCSO3;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_SPCSO4;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Ind1;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Ind2;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Ind3;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_t;
|
||||
extern DataObject iedModel_GenericIO_GGIO1_Ind4;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q;
|
||||
extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t;
|
||||
|
||||
|
||||
|
||||
#define IEDMODEL_GenericIO (&iedModel_GenericIO)
|
||||
#define IEDMODEL_GenericIO_LLN0 (&iedModel_GenericIO_LLN0)
|
||||
#define IEDMODEL_GenericIO_LLN0_Mod (&iedModel_GenericIO_LLN0_Mod)
|
||||
#define IEDMODEL_GenericIO_LLN0_Mod_stVal (&iedModel_GenericIO_LLN0_Mod_stVal)
|
||||
#define IEDMODEL_GenericIO_LLN0_Mod_q (&iedModel_GenericIO_LLN0_Mod_q)
|
||||
#define IEDMODEL_GenericIO_LLN0_Mod_t (&iedModel_GenericIO_LLN0_Mod_t)
|
||||
#define IEDMODEL_GenericIO_LLN0_Mod_ctlModel (&iedModel_GenericIO_LLN0_Mod_ctlModel)
|
||||
#define IEDMODEL_GenericIO_LLN0_Beh (&iedModel_GenericIO_LLN0_Beh)
|
||||
#define IEDMODEL_GenericIO_LLN0_Beh_stVal (&iedModel_GenericIO_LLN0_Beh_stVal)
|
||||
#define IEDMODEL_GenericIO_LLN0_Beh_q (&iedModel_GenericIO_LLN0_Beh_q)
|
||||
#define IEDMODEL_GenericIO_LLN0_Beh_t (&iedModel_GenericIO_LLN0_Beh_t)
|
||||
#define IEDMODEL_GenericIO_LLN0_Health (&iedModel_GenericIO_LLN0_Health)
|
||||
#define IEDMODEL_GenericIO_LLN0_Health_stVal (&iedModel_GenericIO_LLN0_Health_stVal)
|
||||
#define IEDMODEL_GenericIO_LLN0_Health_q (&iedModel_GenericIO_LLN0_Health_q)
|
||||
#define IEDMODEL_GenericIO_LLN0_Health_t (&iedModel_GenericIO_LLN0_Health_t)
|
||||
#define IEDMODEL_GenericIO_LLN0_NamPlt (&iedModel_GenericIO_LLN0_NamPlt)
|
||||
#define IEDMODEL_GenericIO_LLN0_NamPlt_vendor (&iedModel_GenericIO_LLN0_NamPlt_vendor)
|
||||
#define IEDMODEL_GenericIO_LLN0_NamPlt_swRev (&iedModel_GenericIO_LLN0_NamPlt_swRev)
|
||||
#define IEDMODEL_GenericIO_LLN0_NamPlt_d (&iedModel_GenericIO_LLN0_NamPlt_d)
|
||||
#define IEDMODEL_GenericIO_LLN0_NamPlt_configRev (&iedModel_GenericIO_LLN0_NamPlt_configRev)
|
||||
#define IEDMODEL_GenericIO_LLN0_NamPlt_ldNs (&iedModel_GenericIO_LLN0_NamPlt_ldNs)
|
||||
#define IEDMODEL_GenericIO_LPHD1 (&iedModel_GenericIO_LPHD1)
|
||||
#define IEDMODEL_GenericIO_LPHD1_PhyNam (&iedModel_GenericIO_LPHD1_PhyNam)
|
||||
#define IEDMODEL_GenericIO_LPHD1_PhyNam_vendor (&iedModel_GenericIO_LPHD1_PhyNam_vendor)
|
||||
#define IEDMODEL_GenericIO_LPHD1_PhyHealth (&iedModel_GenericIO_LPHD1_PhyHealth)
|
||||
#define IEDMODEL_GenericIO_LPHD1_PhyHealth_stVal (&iedModel_GenericIO_LPHD1_PhyHealth_stVal)
|
||||
#define IEDMODEL_GenericIO_LPHD1_PhyHealth_q (&iedModel_GenericIO_LPHD1_PhyHealth_q)
|
||||
#define IEDMODEL_GenericIO_LPHD1_PhyHealth_t (&iedModel_GenericIO_LPHD1_PhyHealth_t)
|
||||
#define IEDMODEL_GenericIO_LPHD1_Proxy (&iedModel_GenericIO_LPHD1_Proxy)
|
||||
#define IEDMODEL_GenericIO_LPHD1_Proxy_stVal (&iedModel_GenericIO_LPHD1_Proxy_stVal)
|
||||
#define IEDMODEL_GenericIO_LPHD1_Proxy_q (&iedModel_GenericIO_LPHD1_Proxy_q)
|
||||
#define IEDMODEL_GenericIO_LPHD1_Proxy_t (&iedModel_GenericIO_LPHD1_Proxy_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1 (&iedModel_GenericIO_GGIO1)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Mod (&iedModel_GenericIO_GGIO1_Mod)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Mod_q (&iedModel_GenericIO_GGIO1_Mod_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Mod_t (&iedModel_GenericIO_GGIO1_Mod_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Mod_ctlModel (&iedModel_GenericIO_GGIO1_Mod_ctlModel)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Beh (&iedModel_GenericIO_GGIO1_Beh)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Beh_stVal (&iedModel_GenericIO_GGIO1_Beh_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Beh_q (&iedModel_GenericIO_GGIO1_Beh_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Beh_t (&iedModel_GenericIO_GGIO1_Beh_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Health (&iedModel_GenericIO_GGIO1_Health)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Health_stVal (&iedModel_GenericIO_GGIO1_Health_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Health_q (&iedModel_GenericIO_GGIO1_Health_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Health_t (&iedModel_GenericIO_GGIO1_Health_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_NamPlt (&iedModel_GenericIO_GGIO1_NamPlt)
|
||||
#define IEDMODEL_GenericIO_GGIO1_NamPlt_vendor (&iedModel_GenericIO_GGIO1_NamPlt_vendor)
|
||||
#define IEDMODEL_GenericIO_GGIO1_NamPlt_swRev (&iedModel_GenericIO_GGIO1_NamPlt_swRev)
|
||||
#define IEDMODEL_GenericIO_GGIO1_NamPlt_d (&iedModel_GenericIO_GGIO1_NamPlt_d)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn1 (&iedModel_GenericIO_GGIO1_AnIn1)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag (&iedModel_GenericIO_GGIO1_AnIn1_mag)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f (&iedModel_GenericIO_GGIO1_AnIn1_mag_f)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn1_q (&iedModel_GenericIO_GGIO1_AnIn1_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn1_t (&iedModel_GenericIO_GGIO1_AnIn1_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn2 (&iedModel_GenericIO_GGIO1_AnIn2)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag (&iedModel_GenericIO_GGIO1_AnIn2_mag)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f (&iedModel_GenericIO_GGIO1_AnIn2_mag_f)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn2_q (&iedModel_GenericIO_GGIO1_AnIn2_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn2_t (&iedModel_GenericIO_GGIO1_AnIn2_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn3 (&iedModel_GenericIO_GGIO1_AnIn3)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag (&iedModel_GenericIO_GGIO1_AnIn3_mag)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f (&iedModel_GenericIO_GGIO1_AnIn3_mag_f)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn3_q (&iedModel_GenericIO_GGIO1_AnIn3_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn3_t (&iedModel_GenericIO_GGIO1_AnIn3_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn4 (&iedModel_GenericIO_GGIO1_AnIn4)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag (&iedModel_GenericIO_GGIO1_AnIn4_mag)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f (&iedModel_GenericIO_GGIO1_AnIn4_mag_f)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn4_q (&iedModel_GenericIO_GGIO1_AnIn4_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_AnIn4_t (&iedModel_GenericIO_GGIO1_AnIn4_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1 (&iedModel_GenericIO_GGIO1_SPCSO1)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal (&iedModel_GenericIO_GGIO1_SPCSO1_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_q (&iedModel_GenericIO_GGIO1_SPCSO1_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper (&iedModel_GenericIO_GGIO1_SPCSO1_Oper)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_T)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO1_t (&iedModel_GenericIO_GGIO1_SPCSO1_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2 (&iedModel_GenericIO_GGIO1_SPCSO2)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper (&iedModel_GenericIO_GGIO1_SPCSO2_Oper)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_T)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal (&iedModel_GenericIO_GGIO1_SPCSO3_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_q (&iedModel_GenericIO_GGIO1_SPCSO3_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_T)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO3_ctlModel)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO3_t (&iedModel_GenericIO_GGIO1_SPCSO3_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4 (&iedModel_GenericIO_GGIO1_SPCSO4)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal (&iedModel_GenericIO_GGIO1_SPCSO4_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_q (&iedModel_GenericIO_GGIO1_SPCSO4_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper (&iedModel_GenericIO_GGIO1_SPCSO4_Oper)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_T)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO4_ctlModel)
|
||||
#define IEDMODEL_GenericIO_GGIO1_SPCSO4_t (&iedModel_GenericIO_GGIO1_SPCSO4_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind1_t (&iedModel_GenericIO_GGIO1_Ind1_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind2 (&iedModel_GenericIO_GGIO1_Ind2)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind2_stVal (&iedModel_GenericIO_GGIO1_Ind2_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind2_q (&iedModel_GenericIO_GGIO1_Ind2_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind2_t (&iedModel_GenericIO_GGIO1_Ind2_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind3 (&iedModel_GenericIO_GGIO1_Ind3)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind3_stVal (&iedModel_GenericIO_GGIO1_Ind3_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind3_q (&iedModel_GenericIO_GGIO1_Ind3_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind3_t (&iedModel_GenericIO_GGIO1_Ind3_t)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind4 (&iedModel_GenericIO_GGIO1_Ind4)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind4_stVal (&iedModel_GenericIO_GGIO1_Ind4_stVal)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind4_q (&iedModel_GenericIO_GGIO1_Ind4_q)
|
||||
#define IEDMODEL_GenericIO_GGIO1_Ind4_t (&iedModel_GenericIO_GGIO1_Ind4_t)
|
||||
|
||||
#endif /* STATIC_MODEL_H_ */
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* thread_bsd.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
#include "hal_thread.h"
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
|
||||
struct sThread {
|
||||
ThreadExecutionFunction function;
|
||||
void* parameter;
|
||||
pthread_t pthread;
|
||||
int state;
|
||||
bool autodestroy;
|
||||
};
|
||||
|
||||
Semaphore
|
||||
Semaphore_create(int initialValue)
|
||||
{
|
||||
char tmpname[] = {"/tmp/libiec61850.XXXXXX"};
|
||||
mktemp(tmpname);
|
||||
Semaphore self = sem_open(tmpname, O_CREAT, 0666, initialValue);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Wait until semaphore value is more than zero. Then decrease the semaphore value. */
|
||||
void
|
||||
Semaphore_wait(Semaphore self)
|
||||
{
|
||||
sem_wait((sem_t*) self);
|
||||
}
|
||||
|
||||
void
|
||||
Semaphore_post(Semaphore self)
|
||||
{
|
||||
sem_post((sem_t*) self);
|
||||
}
|
||||
|
||||
void
|
||||
Semaphore_destroy(Semaphore self)
|
||||
{
|
||||
sem_close(self);
|
||||
}
|
||||
|
||||
Thread
|
||||
Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
|
||||
{
|
||||
Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));
|
||||
|
||||
if (thread != NULL) {
|
||||
thread->parameter = parameter;
|
||||
thread->function = function;
|
||||
thread->state = 0;
|
||||
thread->autodestroy = autodestroy;
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
static void*
|
||||
destroyAutomaticThread(void* parameter)
|
||||
{
|
||||
Thread thread = (Thread) parameter;
|
||||
|
||||
thread->function(thread->parameter);
|
||||
|
||||
GLOBAL_FREEMEM(thread);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void
|
||||
Thread_start(Thread thread)
|
||||
{
|
||||
if (thread->autodestroy == true) {
|
||||
pthread_create(&thread->pthread, NULL, destroyAutomaticThread, thread);
|
||||
pthread_detach(thread->pthread);
|
||||
}
|
||||
else
|
||||
pthread_create(&thread->pthread, NULL, thread->function, thread->parameter);
|
||||
|
||||
thread->state = 1;
|
||||
}
|
||||
|
||||
void
|
||||
Thread_destroy(Thread thread)
|
||||
{
|
||||
if (thread->state == 1) {
|
||||
pthread_join(thread->pthread, NULL);
|
||||
}
|
||||
|
||||
GLOBAL_FREEMEM(thread);
|
||||
}
|
||||
|
||||
void
|
||||
Thread_sleep(int millies)
|
||||
{
|
||||
usleep(millies * 1000);
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* logging.h
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_
|
||||
#define LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
LogicalNode* parentLN;
|
||||
|
||||
bool locked;
|
||||
|
||||
LogStorage logStorage;
|
||||
|
||||
uint64_t newEntryId;
|
||||
uint64_t newEntryTime;
|
||||
|
||||
uint64_t oldEntryId;
|
||||
uint64_t oldEntryTime;
|
||||
|
||||
} LogInstance;
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
LogControlBlock* logControlBlock;
|
||||
MmsMapping* mmsMapping;
|
||||
|
||||
DataSet* dataSet;
|
||||
char* dataSetRef;
|
||||
bool isDynamicDataSet;
|
||||
|
||||
LogicalNode* logicalNode;
|
||||
MmsDomain* domain;
|
||||
|
||||
MmsValue* mmsValue;
|
||||
MmsVariableSpecification* mmsType;
|
||||
|
||||
MmsValue* oldEntr;
|
||||
MmsValue* oldEntrTm;
|
||||
MmsValue* newEntr;
|
||||
MmsValue* newEntrTm;
|
||||
|
||||
LogInstance* logInstance;
|
||||
|
||||
bool enabled;
|
||||
|
||||
uint64_t nextIntegrityScan;
|
||||
|
||||
int triggerOps;
|
||||
uint32_t intgPd;
|
||||
|
||||
} LogControl;
|
||||
|
||||
|
||||
LogInstance*
|
||||
LogInstance_create(LogicalNode* parentLN, const char* name);
|
||||
|
||||
void
|
||||
LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage);
|
||||
|
||||
void
|
||||
LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* value, uint8_t flag);
|
||||
|
||||
uint64_t
|
||||
LogInstance_logEntryStart(LogInstance* self);
|
||||
|
||||
void
|
||||
LogInstance_logEntryData(LogInstance* self, uint64_t entryID, const char* dataRef, MmsValue* value, uint8_t flag);
|
||||
|
||||
void
|
||||
LogInstance_logEntryFinished(LogInstance* self, uint64_t entryID);
|
||||
|
||||
void
|
||||
LogInstance_destroy(LogInstance* self);
|
||||
|
||||
LogControl*
|
||||
LogControl_create(LogicalNode* parentLN, MmsMapping* mmsMapping);
|
||||
|
||||
void
|
||||
LogControl_setLog(LogControl* self, LogInstance* logInstance);
|
||||
|
||||
void
|
||||
LogControl_destroy(LogControl* self);
|
||||
|
||||
void
|
||||
LogControl_setLogStorage(LogControl* self, LogStorage logStorage);
|
||||
|
||||
MmsVariableSpecification*
|
||||
Logging_createLCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode,
|
||||
int lcbCount);
|
||||
|
||||
void
|
||||
Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs);
|
||||
|
||||
MmsValue*
|
||||
LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig);
|
||||
|
||||
MmsDataAccessError
|
||||
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
|
||||
MmsValue* value, MmsServerConnection connection);
|
||||
|
||||
#endif /* LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_ */
|
||||
@ -0,0 +1,947 @@
|
||||
/*
|
||||
* logging.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
#include "stack_config.h"
|
||||
#include "mms_mapping.h"
|
||||
#include "logging.h"
|
||||
#include "linked_list.h"
|
||||
#include "array_list.h"
|
||||
#include "hal_thread.h"
|
||||
|
||||
#include "simple_allocator.h"
|
||||
#include "mem_alloc_linked_list.h"
|
||||
|
||||
#include "mms_mapping_internal.h"
|
||||
#include "mms_value_internal.h"
|
||||
|
||||
#include "logging_api.h"
|
||||
|
||||
#if (CONFIG_IEC61850_LOG_SERVICE == 1)
|
||||
|
||||
LogInstance*
|
||||
LogInstance_create(LogicalNode* parentLN, const char* name)
|
||||
{
|
||||
LogInstance* self = (LogInstance*) GLOBAL_MALLOC(sizeof(LogInstance));
|
||||
|
||||
self->name = copyString(name);
|
||||
self->parentLN = parentLN;
|
||||
self->logStorage = NULL;
|
||||
self->locked = false;
|
||||
|
||||
self->oldEntryId = 0;
|
||||
self->oldEntryTime = 0;
|
||||
self->newEntryId = 0;
|
||||
self->newEntryTime = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
LogInstance_destroy(LogInstance* self)
|
||||
{
|
||||
GLOBAL_FREEMEM(self->name);
|
||||
GLOBAL_FREEMEM(self);
|
||||
}
|
||||
|
||||
void
|
||||
LogInstance_logSingleData(LogInstance* self, const char* dataRef, MmsValue* value, uint8_t flag)
|
||||
{
|
||||
LogStorage logStorage = self->logStorage;
|
||||
|
||||
if (logStorage != NULL) {
|
||||
|
||||
while (self->locked)
|
||||
Thread_sleep(1);
|
||||
|
||||
self->locked = true;
|
||||
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: Log value - dataRef: %s flag: %i\n", dataRef, flag);
|
||||
|
||||
uint64_t timestamp = Hal_getTimeInMs();
|
||||
|
||||
uint64_t entryID = LogStorage_addEntry(logStorage, timestamp);
|
||||
|
||||
int dataSize = MmsValue_encodeMmsData(value, NULL, 0, false);
|
||||
|
||||
uint8_t* data = (uint8_t*) GLOBAL_MALLOC(dataSize);
|
||||
|
||||
MmsValue_encodeMmsData(value, data, 0, true);
|
||||
|
||||
LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag);
|
||||
|
||||
self->locked = false;
|
||||
|
||||
GLOBAL_FREEMEM(data);
|
||||
|
||||
self->newEntryId = entryID;
|
||||
self->newEntryTime = timestamp;
|
||||
|
||||
}
|
||||
else
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: no log storage available for logging!\n");
|
||||
}
|
||||
|
||||
uint64_t
|
||||
LogInstance_logEntryStart(LogInstance* self)
|
||||
{
|
||||
LogStorage logStorage = self->logStorage;
|
||||
|
||||
if (logStorage != NULL) {
|
||||
|
||||
while (self->locked)
|
||||
Thread_sleep(1);
|
||||
|
||||
self->locked = true;
|
||||
|
||||
uint64_t timestamp = Hal_getTimeInMs();
|
||||
|
||||
uint64_t entryID = LogStorage_addEntry(logStorage, timestamp);
|
||||
|
||||
return entryID;
|
||||
}
|
||||
else {
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: no log storage available for logging!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LogInstance_logEntryData(LogInstance* self, uint64_t entryID, const char* dataRef, MmsValue* value, uint8_t flag)
|
||||
{
|
||||
LogStorage logStorage = self->logStorage;
|
||||
|
||||
if (logStorage != NULL) {
|
||||
|
||||
int dataSize = MmsValue_encodeMmsData(value, NULL, 0, false);
|
||||
|
||||
uint8_t* data = (uint8_t*) GLOBAL_MALLOC(dataSize);
|
||||
|
||||
MmsValue_encodeMmsData(value, data, 0, true);
|
||||
|
||||
LogStorage_addEntryData(logStorage, entryID, dataRef, data, dataSize, flag);
|
||||
|
||||
self->locked = false;
|
||||
|
||||
GLOBAL_FREEMEM(data);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LogInstance_logEntryFinished(LogInstance* self, uint64_t entryID)
|
||||
{
|
||||
self->locked = false;
|
||||
}
|
||||
|
||||
void
|
||||
LogInstance_setLogStorage(LogInstance* self, LogStorage logStorage)
|
||||
{
|
||||
self->logStorage = logStorage;
|
||||
|
||||
LogStorage_getOldestAndNewestEntries(logStorage, &(self->newEntryId), &(self->newEntryTime),
|
||||
&(self->oldEntryId), &(self->oldEntryTime));
|
||||
}
|
||||
|
||||
LogControl*
|
||||
LogControl_create(LogicalNode* parentLN, MmsMapping* mmsMapping)
|
||||
{
|
||||
LogControl* self = (LogControl*) GLOBAL_MALLOC(sizeof(LogControl));
|
||||
|
||||
self->enabled = false;
|
||||
self->dataSet = NULL;
|
||||
self->isDynamicDataSet = false;
|
||||
self->triggerOps = 0;
|
||||
self->logicalNode = parentLN;
|
||||
self->mmsMapping = mmsMapping;
|
||||
self->dataSetRef = NULL;
|
||||
self->logInstance = NULL;
|
||||
self->intgPd = 0;
|
||||
self->nextIntegrityScan = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
LogControl_destroy(LogControl* self)
|
||||
{
|
||||
if (self != NULL) {
|
||||
|
||||
MmsValue_delete(self->mmsValue);
|
||||
GLOBAL_FREEMEM(self->name);
|
||||
|
||||
if (self->dataSetRef != NULL)
|
||||
GLOBAL_FREEMEM(self->dataSetRef);
|
||||
|
||||
GLOBAL_FREEMEM(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LogControl_setLog(LogControl* self, LogInstance* logInstance)
|
||||
{
|
||||
self->logInstance = logInstance;
|
||||
}
|
||||
|
||||
static void
|
||||
prepareLogControl(LogControl* logControl)
|
||||
{
|
||||
if (logControl->dataSetRef == NULL) {
|
||||
logControl->enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DataSet* dataSet = IedModel_lookupDataSet(logControl->mmsMapping->model, logControl->dataSetRef);
|
||||
|
||||
if (dataSet == NULL)
|
||||
return;
|
||||
else
|
||||
logControl->dataSet = dataSet;
|
||||
}
|
||||
|
||||
static bool
|
||||
enableLogging(LogControl* self)
|
||||
{
|
||||
if ((self->dataSet != NULL) && (self->logInstance != NULL)) {
|
||||
self->enabled = true;
|
||||
|
||||
if ((self->triggerOps & TRG_OPT_INTEGRITY) && (self->intgPd != 0))
|
||||
self->nextIntegrityScan = Hal_getTimeInMs();
|
||||
else
|
||||
self->nextIntegrityScan = 0;
|
||||
|
||||
MmsValue* enabled = MmsValue_getSubElement(self->mmsValue, self->mmsType, "LogEna");
|
||||
|
||||
MmsValue_setBoolean(enabled, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static LogControlBlock*
|
||||
getLCBForLogicalNodeWithIndex(MmsMapping* self, LogicalNode* logicalNode, int index)
|
||||
{
|
||||
int lcbCount = 0;
|
||||
|
||||
LogControlBlock* nextLcb = self->model->lcbs;
|
||||
|
||||
while (nextLcb != NULL ) {
|
||||
if (nextLcb->parent == logicalNode) {
|
||||
|
||||
if (lcbCount == index)
|
||||
return nextLcb;
|
||||
|
||||
lcbCount++;
|
||||
|
||||
}
|
||||
|
||||
nextLcb = nextLcb->sibling;
|
||||
}
|
||||
|
||||
return NULL ;
|
||||
}
|
||||
|
||||
static LogControl*
|
||||
lookupLogControl(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName)
|
||||
{
|
||||
LinkedList element = LinkedList_getNext(self->logControls);
|
||||
|
||||
while (element != NULL) {
|
||||
LogControl* logControl = (LogControl*) element->data;
|
||||
|
||||
if (logControl->domain == domain) {
|
||||
if (strcmp(logControl->logicalNode->name, lnName) == 0) {
|
||||
if (strcmp(logControl->logControlBlock->name, objectName) == 0) {
|
||||
return logControl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
element = LinkedList_getNext(element);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static LogInstance*
|
||||
getLogInstanceByLogRef(MmsMapping* self, const char* logRef)
|
||||
{
|
||||
char refStr[130];
|
||||
char* domainName;
|
||||
char* lnName;
|
||||
char* logName;
|
||||
|
||||
strncpy(refStr, logRef, 129);
|
||||
|
||||
domainName = refStr;
|
||||
|
||||
lnName = strchr(refStr, '/');
|
||||
|
||||
if (lnName == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((lnName - domainName) > 64)
|
||||
return NULL;
|
||||
|
||||
lnName[0] = 0;
|
||||
lnName++;
|
||||
|
||||
logName = strchr(lnName, '$');
|
||||
|
||||
if (logName == NULL)
|
||||
return NULL;
|
||||
|
||||
logName[0] = 0;
|
||||
logName++;
|
||||
|
||||
LinkedList instance = LinkedList_getNext(self->logInstances);
|
||||
|
||||
while (instance != NULL) {
|
||||
|
||||
LogInstance* logInstance = (LogInstance*) LinkedList_getData(instance);
|
||||
|
||||
if (strcmp(logInstance->name, logName) == 0) {
|
||||
|
||||
if (strcmp(lnName, logInstance->parentLN->name) == 0) {
|
||||
LogicalDevice* ld = (LogicalDevice*) logInstance->parentLN->parent;
|
||||
|
||||
if (strcmp(ld->name, domainName) == 0)
|
||||
return logInstance;
|
||||
}
|
||||
}
|
||||
|
||||
instance = LinkedList_getNext(instance);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
updateLogStatusInLCB(LogControl* self)
|
||||
{
|
||||
LogInstance* logInstance = self->logInstance;
|
||||
|
||||
if (logInstance != NULL) {
|
||||
MmsValue_setBinaryTime(self->oldEntrTm, logInstance->oldEntryTime);
|
||||
MmsValue_setBinaryTime(self->newEntrTm, logInstance->newEntryTime);
|
||||
|
||||
MmsValue_setOctetString(self->oldEntr, (uint8_t*) &(logInstance->oldEntryId), 8);
|
||||
MmsValue_setOctetString(self->newEntr, (uint8_t*) &(logInstance->newEntryId), 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
freeDynamicDataSet(LogControl* self)
|
||||
{
|
||||
if (self->isDynamicDataSet) {
|
||||
if (self->dataSet != NULL) {
|
||||
MmsMapping_freeDynamicallyCreatedDataSet(self->dataSet);
|
||||
self->isDynamicDataSet = false;
|
||||
self->dataSet = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MmsDataAccessError
|
||||
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
|
||||
MmsValue* value, MmsServerConnection connection)
|
||||
{
|
||||
bool updateValue = false;
|
||||
|
||||
char variableId[130];
|
||||
|
||||
strncpy(variableId, variableIdOrig, 129);
|
||||
|
||||
char* separator = strchr(variableId, '$');
|
||||
|
||||
*separator = 0;
|
||||
|
||||
char* lnName = variableId;
|
||||
|
||||
if (lnName == NULL)
|
||||
return DATA_ACCESS_ERROR_INVALID_ADDRESS;
|
||||
|
||||
char* objectName = MmsMapping_getNextNameElement(separator + 1);
|
||||
|
||||
if (objectName == NULL)
|
||||
return DATA_ACCESS_ERROR_INVALID_ADDRESS;
|
||||
|
||||
char* varName = MmsMapping_getNextNameElement(objectName);
|
||||
|
||||
if (varName != NULL)
|
||||
*(varName - 1) = 0;
|
||||
|
||||
LogControl* logControl = lookupLogControl(self, domain, lnName, objectName);
|
||||
|
||||
if (logControl == NULL) {
|
||||
return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
|
||||
}
|
||||
|
||||
if (strcmp(varName, "LogEna") == 0) {
|
||||
bool logEna = MmsValue_getBoolean(value);
|
||||
|
||||
if (logEna == false) {
|
||||
logControl->enabled = false;
|
||||
}
|
||||
else {
|
||||
|
||||
if (enableLogging(logControl)) {
|
||||
logControl->enabled = true;
|
||||
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: enabled log control %s\n", logControl->name);
|
||||
}
|
||||
else
|
||||
return DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
|
||||
}
|
||||
|
||||
updateValue = true;
|
||||
}
|
||||
else if (strcmp(varName, "LogRef") == 0) {
|
||||
|
||||
if (logControl->enabled == false) {
|
||||
/* check if logRef is valid or NULL */
|
||||
const char* logRef = MmsValue_toString(value);
|
||||
|
||||
if (logRef == NULL) {
|
||||
|
||||
logControl->logInstance = NULL;
|
||||
|
||||
updateValue = true;
|
||||
}
|
||||
else {
|
||||
if (strcmp(logRef, "") == 0) {
|
||||
logControl->logInstance = NULL;
|
||||
updateValue = true;
|
||||
}
|
||||
else {
|
||||
|
||||
/* remove IED name from logRef */
|
||||
char* iedName = self->mmsDevice->deviceName;
|
||||
|
||||
uint32_t iedNameLen = strlen(iedName);
|
||||
|
||||
if (iedNameLen < strlen(logRef)) {
|
||||
if (memcmp(iedName, logRef, iedNameLen) == 0) {
|
||||
logRef = logRef + iedNameLen;
|
||||
}
|
||||
}
|
||||
|
||||
LogInstance* logInstance = getLogInstanceByLogRef(self, logRef);
|
||||
|
||||
if (logInstance != NULL) {
|
||||
|
||||
logControl->logInstance = logInstance;
|
||||
updateValue = true;
|
||||
}
|
||||
else
|
||||
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
|
||||
}
|
||||
else if (strcmp(varName, "DatSet") == 0) {
|
||||
|
||||
if (logControl->enabled == false) {
|
||||
/* check if datSet is valid or NULL/empty */
|
||||
const char* dataSetRef = MmsValue_toString(value);
|
||||
|
||||
if (strlen(dataSetRef) == 0) {
|
||||
logControl->dataSet = NULL;
|
||||
updateValue = true;
|
||||
}
|
||||
else {
|
||||
DataSet* dataSet = IedModel_lookupDataSet(logControl->mmsMapping->model, dataSetRef);
|
||||
|
||||
if (dataSet != NULL) {
|
||||
freeDynamicDataSet(logControl);
|
||||
|
||||
logControl->dataSet = dataSet;
|
||||
updateValue = true;
|
||||
|
||||
}
|
||||
|
||||
#if (MMS_DYNAMIC_DATA_SETS == 1)
|
||||
|
||||
if (dataSet == NULL) {
|
||||
|
||||
dataSet = MmsMapping_getDomainSpecificDataSet(self, dataSetRef);
|
||||
|
||||
if (dataSet == NULL) {
|
||||
|
||||
if (dataSetRef[0] == '/') { /* check for VMD specific data set */
|
||||
MmsNamedVariableList mmsVariableList =
|
||||
MmsDevice_getNamedVariableListWithName(self->mmsDevice, dataSetRef + 1);
|
||||
|
||||
if (mmsVariableList != NULL)
|
||||
dataSet = MmsMapping_createDataSetByNamedVariableList(self, mmsVariableList);
|
||||
}
|
||||
}
|
||||
|
||||
if (dataSet != NULL) {
|
||||
freeDynamicDataSet(logControl);
|
||||
logControl->dataSet = dataSet;
|
||||
logControl->isDynamicDataSet = true;
|
||||
|
||||
updateValue = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /*(MMS_DYNAMIC_DATA_SETS == 1) */
|
||||
|
||||
if (dataSet == NULL) {
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: data set (%s) not found!\n", logControl->dataSetRef);
|
||||
return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
|
||||
}
|
||||
else if (strcmp(varName, "IntgPd") == 0) {
|
||||
if (logControl->enabled == false) {
|
||||
logControl->intgPd = MmsValue_toUint32(value);
|
||||
updateValue = true;
|
||||
}
|
||||
else
|
||||
return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
|
||||
}
|
||||
else if (strcmp(varName, "TrgOps") == 0) {
|
||||
if (logControl->enabled == false) {
|
||||
logControl->triggerOps = (MmsValue_getBitStringAsInteger(value) / 2);
|
||||
updateValue = true;
|
||||
}
|
||||
else
|
||||
return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (updateValue) {
|
||||
MmsValue* element = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName);
|
||||
|
||||
MmsValue_update(element, value);
|
||||
|
||||
return DATA_ACCESS_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
MmsValue*
|
||||
LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig)
|
||||
{
|
||||
MmsValue* value = NULL;
|
||||
|
||||
char variableId[130];
|
||||
|
||||
strncpy(variableId, variableIdOrig, 129);
|
||||
|
||||
char* separator = strchr(variableId, '$');
|
||||
|
||||
*separator = 0;
|
||||
|
||||
char* lnName = variableId;
|
||||
|
||||
if (lnName == NULL)
|
||||
return NULL;
|
||||
|
||||
char* objectName = MmsMapping_getNextNameElement(separator + 1);
|
||||
|
||||
if (objectName == NULL)
|
||||
return NULL;
|
||||
|
||||
char* varName = MmsMapping_getNextNameElement(objectName);
|
||||
|
||||
if (varName != NULL)
|
||||
*(varName - 1) = 0;
|
||||
|
||||
LogControl* logControl = lookupLogControl(self, domain, lnName, objectName);
|
||||
|
||||
if (logControl != NULL) {
|
||||
|
||||
updateLogStatusInLCB(logControl);
|
||||
|
||||
if (varName != NULL) {
|
||||
value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName);
|
||||
}
|
||||
else {
|
||||
value = logControl->mmsValue;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl)
|
||||
{
|
||||
char* dataSetReference;
|
||||
|
||||
char* domainName = MmsDomain_getName(logControl->domain);
|
||||
char* lnName = lcb->parent->name;
|
||||
|
||||
dataSetReference = createString(5, domainName, "/", lnName, "$", lcb->dataSetName);
|
||||
|
||||
return dataSetReference;
|
||||
}
|
||||
|
||||
static MmsValue*
|
||||
createTrgOps(LogControlBlock* logControlBlock) {
|
||||
MmsValue* trgOps = MmsValue_newBitString(-6);
|
||||
|
||||
uint8_t triggerOps = logControlBlock->trgOps;
|
||||
|
||||
if (triggerOps & TRG_OPT_DATA_CHANGED)
|
||||
MmsValue_setBitStringBit(trgOps, 1, true);
|
||||
if (triggerOps & TRG_OPT_QUALITY_CHANGED)
|
||||
MmsValue_setBitStringBit(trgOps, 2, true);
|
||||
if (triggerOps & TRG_OPT_DATA_UPDATE)
|
||||
MmsValue_setBitStringBit(trgOps, 3, true);
|
||||
if (triggerOps & TRG_OPT_INTEGRITY)
|
||||
MmsValue_setBitStringBit(trgOps, 4, true);
|
||||
|
||||
return trgOps;
|
||||
}
|
||||
|
||||
static void
|
||||
LogControl_updateLogEna(LogControl* self)
|
||||
{
|
||||
MmsValue_setBoolean(MmsValue_getElement(self->mmsValue, 0), self->enabled);
|
||||
}
|
||||
|
||||
static MmsVariableSpecification*
|
||||
createLogControlBlock(MmsMapping* self, LogControlBlock* logControlBlock,
|
||||
LogControl* logControl)
|
||||
{
|
||||
MmsVariableSpecification* lcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
lcb->name = copyString(logControlBlock->name);
|
||||
lcb->type = MMS_STRUCTURE;
|
||||
|
||||
MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue));
|
||||
mmsValue->deleteValue = false;
|
||||
mmsValue->type = MMS_STRUCTURE;
|
||||
|
||||
int structSize = 9;
|
||||
|
||||
mmsValue->value.structure.size = structSize;
|
||||
mmsValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(structSize, sizeof(MmsValue*));
|
||||
|
||||
lcb->typeSpec.structure.elementCount = structSize;
|
||||
|
||||
lcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(structSize,
|
||||
sizeof(MmsVariableSpecification*));
|
||||
|
||||
/* LogEna */
|
||||
MmsVariableSpecification* namedVariable =
|
||||
(MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
|
||||
namedVariable->name = copyString("LogEna");
|
||||
namedVariable->type = MMS_BOOLEAN;
|
||||
|
||||
lcb->typeSpec.structure.elements[0] = namedVariable;
|
||||
mmsValue->value.structure.components[0] = MmsValue_newBoolean(logControlBlock->logEna);
|
||||
|
||||
/* LogRef */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("LogRef");
|
||||
namedVariable->typeSpec.visibleString = -129;
|
||||
namedVariable->type = MMS_VISIBLE_STRING;
|
||||
lcb->typeSpec.structure.elements[1] = namedVariable;
|
||||
|
||||
if (logControlBlock->logRef != NULL) {
|
||||
char logRef[130];
|
||||
|
||||
int maxLogRefLength = 129 - strlen(self->model->name);
|
||||
|
||||
strcpy(logRef, self->model->name);
|
||||
strncat(logRef, logControlBlock->logRef, maxLogRefLength);
|
||||
|
||||
mmsValue->value.structure.components[1] = MmsValue_newVisibleString(logRef);
|
||||
}
|
||||
else {
|
||||
char* logRef = createString(4, logControl->domain->domainName, "/", logControlBlock->parent->name,
|
||||
"$GeneralLog");
|
||||
|
||||
mmsValue->value.structure.components[1] = MmsValue_newVisibleString(logRef);
|
||||
|
||||
GLOBAL_FREEMEM(logRef);
|
||||
}
|
||||
|
||||
/* DatSet */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("DatSet");
|
||||
namedVariable->typeSpec.visibleString = -129;
|
||||
namedVariable->type = MMS_VISIBLE_STRING;
|
||||
lcb->typeSpec.structure.elements[2] = namedVariable;
|
||||
|
||||
if (logControlBlock->dataSetName != NULL) {
|
||||
char* dataSetReference = createDataSetReferenceForDefaultDataSet(logControlBlock, logControl);
|
||||
|
||||
logControl->dataSetRef = dataSetReference;
|
||||
|
||||
mmsValue->value.structure.components[2] = MmsValue_newVisibleString(dataSetReference);
|
||||
}
|
||||
else
|
||||
mmsValue->value.structure.components[2] = MmsValue_newVisibleString("");
|
||||
|
||||
/* OldEntrTm */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("OldEntrTm");
|
||||
namedVariable->type = MMS_BINARY_TIME;
|
||||
namedVariable->typeSpec.binaryTime = 6;
|
||||
lcb->typeSpec.structure.elements[3] = namedVariable;
|
||||
|
||||
mmsValue->value.structure.components[3] = MmsValue_newBinaryTime(false);
|
||||
|
||||
logControl->oldEntrTm = mmsValue->value.structure.components[3];
|
||||
|
||||
/* NewEntrTm */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("NewEntrTm");
|
||||
namedVariable->type = MMS_BINARY_TIME;
|
||||
namedVariable->typeSpec.binaryTime = 6;
|
||||
lcb->typeSpec.structure.elements[4] = namedVariable;
|
||||
|
||||
mmsValue->value.structure.components[4] = MmsValue_newBinaryTime(false);
|
||||
|
||||
logControl->newEntrTm = mmsValue->value.structure.components[4];
|
||||
|
||||
/* OldEntr */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("OldEntr");
|
||||
namedVariable->type = MMS_OCTET_STRING;
|
||||
namedVariable->typeSpec.octetString = 8;
|
||||
|
||||
lcb->typeSpec.structure.elements[5] = namedVariable;
|
||||
|
||||
mmsValue->value.structure.components[5] = MmsValue_newOctetString(8, 8);
|
||||
|
||||
logControl->oldEntr = mmsValue->value.structure.components[5];
|
||||
|
||||
/* NewEntr */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("NewEntr");
|
||||
namedVariable->type = MMS_OCTET_STRING;
|
||||
namedVariable->typeSpec.octetString = 8;
|
||||
|
||||
lcb->typeSpec.structure.elements[6] = namedVariable;
|
||||
|
||||
mmsValue->value.structure.components[6] = MmsValue_newOctetString(8, 8);
|
||||
|
||||
logControl->newEntr = mmsValue->value.structure.components[6];
|
||||
|
||||
/* TrgOps */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("TrgOps");
|
||||
namedVariable->type = MMS_BIT_STRING;
|
||||
namedVariable->typeSpec.bitString = -6;
|
||||
lcb->typeSpec.structure.elements[7] = namedVariable;
|
||||
mmsValue->value.structure.components[7] = createTrgOps(logControlBlock);
|
||||
|
||||
/* IntgPd */
|
||||
namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("IntgPd");
|
||||
namedVariable->type = MMS_UNSIGNED;
|
||||
namedVariable->typeSpec.unsignedInteger = 32;
|
||||
lcb->typeSpec.structure.elements[8] = namedVariable;
|
||||
mmsValue->value.structure.components[8] =
|
||||
MmsValue_newUnsignedFromUint32(logControlBlock->intPeriod);
|
||||
|
||||
logControl->intgPd = logControlBlock->intPeriod;
|
||||
|
||||
logControl->mmsType = lcb;
|
||||
logControl->mmsValue = mmsValue;
|
||||
logControl->logControlBlock = logControlBlock;
|
||||
logControl->triggerOps = logControlBlock->trgOps;
|
||||
|
||||
logControl->enabled = logControlBlock->logEna;
|
||||
|
||||
prepareLogControl(logControl);
|
||||
|
||||
if (logControl->enabled)
|
||||
enableLogging(logControl);
|
||||
|
||||
LogControl_updateLogEna(logControl);
|
||||
|
||||
return lcb;
|
||||
}
|
||||
|
||||
MmsVariableSpecification*
|
||||
Logging_createLCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode,
|
||||
int lcbCount)
|
||||
{
|
||||
MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1,
|
||||
sizeof(MmsVariableSpecification));
|
||||
namedVariable->name = copyString("LG");
|
||||
namedVariable->type = MMS_STRUCTURE;
|
||||
|
||||
namedVariable->typeSpec.structure.elementCount = lcbCount;
|
||||
namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(lcbCount,
|
||||
sizeof(MmsVariableSpecification*));
|
||||
|
||||
int currentLcb = 0;
|
||||
|
||||
while (currentLcb < lcbCount) {
|
||||
|
||||
LogControl* logControl = LogControl_create(logicalNode, self);
|
||||
|
||||
LogControlBlock* logControlBlock = getLCBForLogicalNodeWithIndex(self, logicalNode, currentLcb);
|
||||
|
||||
logControl->name = createString(3, logicalNode->name, "$LG$", logControlBlock->name);
|
||||
logControl->domain = domain;
|
||||
|
||||
namedVariable->typeSpec.structure.elements[currentLcb] =
|
||||
createLogControlBlock(self, logControlBlock, logControl);
|
||||
|
||||
if (logControlBlock->logRef != NULL)
|
||||
logControl->logInstance = getLogInstanceByLogRef(self, logControlBlock->logRef);
|
||||
|
||||
LinkedList_add(self->logControls, logControl);
|
||||
|
||||
currentLcb++;
|
||||
}
|
||||
|
||||
return namedVariable;
|
||||
}
|
||||
|
||||
static void
|
||||
LogControl_logAllDatasetEntries(LogControl* self, const char* iedName)
|
||||
{
|
||||
if (self->dataSet == NULL)
|
||||
return;
|
||||
|
||||
if (self->logInstance != NULL) {
|
||||
|
||||
char dataRef[130];
|
||||
|
||||
LogInstance* log = self->logInstance;
|
||||
|
||||
uint64_t entryID = LogInstance_logEntryStart(log);
|
||||
|
||||
DataSetEntry* dataSetEntry = self->dataSet->fcdas;
|
||||
|
||||
while (dataSetEntry != NULL) {
|
||||
|
||||
sprintf(dataRef, "%s%s/%s", iedName, dataSetEntry->logicalDeviceName, dataSetEntry->variableName);
|
||||
|
||||
LogInstance_logEntryData(log, entryID, dataRef, dataSetEntry->value, TRG_OPT_INTEGRITY * 2);
|
||||
|
||||
dataSetEntry = dataSetEntry->sibling;
|
||||
}
|
||||
|
||||
LogInstance_logEntryFinished(log, entryID);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs)
|
||||
{
|
||||
LinkedList logControlElem = LinkedList_getNext(self->logControls);
|
||||
|
||||
while (logControlElem != NULL) {
|
||||
|
||||
LogControl* logControl = (LogControl*) LinkedList_getData(logControlElem);
|
||||
|
||||
if (logControl->enabled) {
|
||||
|
||||
if (logControl->nextIntegrityScan != 0) {
|
||||
|
||||
if (currentTimeInMs >= logControl->nextIntegrityScan) {
|
||||
|
||||
//if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: INTEGRITY SCAN for log %s\n", logControl->name);
|
||||
|
||||
LogControl_logAllDatasetEntries(logControl, self->mmsDevice->deviceName);
|
||||
|
||||
logControl->nextIntegrityScan += logControl->intgPd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logControlElem = LinkedList_getNext(logControlElem);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logStorage)
|
||||
{
|
||||
LogInstance* logInstance = getLogInstanceByLogRef(self, logRef);
|
||||
|
||||
if (logInstance != NULL) {
|
||||
LogInstance_setLogStorage(logInstance, logStorage);
|
||||
|
||||
char domainName[65];
|
||||
|
||||
MmsMapping_getMmsDomainFromObjectReference(logRef, domainName);
|
||||
|
||||
char domainNameWithIEDName[65];
|
||||
|
||||
strcpy(domainNameWithIEDName, self->model->name);
|
||||
strcat(domainNameWithIEDName, domainName);
|
||||
|
||||
MmsDomain* mmsDomain = MmsDevice_getDomain(self->mmsDevice, domainNameWithIEDName);
|
||||
|
||||
if (mmsDomain == NULL) {
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: MmsMapping_setLogStorage: domain %s not found!\n", domainNameWithIEDName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MmsJournal mmsJournal = NULL;
|
||||
|
||||
const char* logName = strchr(logRef, '/');
|
||||
|
||||
if (logName != NULL) {
|
||||
logName += 1;
|
||||
mmsJournal = MmsDomain_getJournal(mmsDomain, logName);
|
||||
}
|
||||
|
||||
if (mmsJournal != NULL)
|
||||
mmsJournal->logStorage = logStorage;
|
||||
else
|
||||
if (DEBUG_IED_SERVER)
|
||||
printf("IED_SERVER: Failed to retrieve MMS journal for log!\n");
|
||||
}
|
||||
|
||||
if (DEBUG_IED_SERVER)
|
||||
if (logInstance == NULL)
|
||||
printf("IED_SERVER: MmsMapping_setLogStorage no matching log for %s found!\n", logRef);
|
||||
}
|
||||
|
||||
#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
This directory contains the log driver implementation that implements the logging API. The logging API is a service provider interface that has to be implemented by the log driver.
|
||||
An application can use different log drivers at the same time.
|
||||
Each log driver provides a public constructor for the driver specific implementation of the LogStorage class. The constructor has to fill the virtual function table of the LogStorage instance with its own implementation functions.
|
||||
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* log_storage_sqlite.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
|
||||
#include "logging_api.h"
|
||||
#include "libiec61850_platform_includes.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#ifndef DEBUG_LOG_STORAGE_DRIVER
|
||||
#define DEBUG_LOG_STORAGE_DRIVER 0
|
||||
#endif
|
||||
|
||||
static uint64_t
|
||||
SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp);
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
|
||||
uint64_t* oldEntry, uint64_t* oldEntryTime);
|
||||
|
||||
static void
|
||||
SqliteLogStorage_destroy(LogStorage self);
|
||||
|
||||
|
||||
typedef struct sSqliteLogStorage {
|
||||
char* filename;
|
||||
sqlite3* db;
|
||||
sqlite3_stmt* insertEntryStmt;
|
||||
sqlite3_stmt* insertEntryDataStmt;
|
||||
sqlite3_stmt* getEntriesWithRange;
|
||||
sqlite3_stmt* getEntriesAfter;
|
||||
sqlite3_stmt* getEntryData;
|
||||
sqlite3_stmt* getOldEntry;
|
||||
sqlite3_stmt* getNewEntry;
|
||||
} SqliteLogStorage;
|
||||
|
||||
static const char* CREATE_TABLE_ENTRYS = "create table if not exists Entries (entryID integer primary key, timeOfEntry integer)";
|
||||
static const char* CREATE_TABLE_ENTRY_DATA = "create table if not exists EntryData (entryID integer, dataRef text, value blob, reasonCode integer)";
|
||||
static const char* INSERT_ENTRY = "insert into Entries (timeOfEntry) values (?)";
|
||||
static const char* INSERT_ENTRY_DATA = "insert into EntryData (entryID, dataRef, value, reasonCode) values (?,?,?,?)";
|
||||
static const char* GET_ENTRIES_WITH_RANGE = "select entryID, timeOfEntry from Entries where timeOfEntry >= ? and timeOfEntry <= ?";
|
||||
static const char* GET_ENTRIES_AFTER = "select entryID, timeOfEntry from Entries where entryID > ?";
|
||||
static const char* GET_ENTRY_DATA = "select dataRef, value, reasonCode from EntryData where entryID = ?";
|
||||
|
||||
static const char* GET_OLD_ENTRY = "select * from Entries where entryID = (select min(entryID) from Entries where timeOfEntry = (select min(timeOfEntry) from Entries))";
|
||||
static const char* GET_NEW_ENTRY = "select * from Entries where entryID = (select max(entryID) from Entries where timeOfEntry = (select max(timeOfEntry) from Entries))";
|
||||
|
||||
static char*
|
||||
copyStringInternal(const char* string)
|
||||
{
|
||||
int newStringLength = strlen(string) + 1;
|
||||
|
||||
char* newString = (char*) malloc(newStringLength);
|
||||
|
||||
memcpy(newString, string, newStringLength);
|
||||
|
||||
return newString;
|
||||
}
|
||||
|
||||
LogStorage
|
||||
SqliteLogStorage_createInstance(const char* filename)
|
||||
{
|
||||
|
||||
sqlite3* db = NULL;
|
||||
sqlite3_stmt* insertEntryStmt = NULL;
|
||||
sqlite3_stmt* insertEntryDataStmt = NULL;
|
||||
sqlite3_stmt* getEntriesWithRange = NULL;
|
||||
sqlite3_stmt* getEntriesAfter = NULL;
|
||||
sqlite3_stmt* getEntryData = NULL;
|
||||
sqlite3_stmt* getOldEntry = NULL;
|
||||
sqlite3_stmt* getNewEntry = NULL;
|
||||
char *zErrMsg = 0;
|
||||
|
||||
int rc = sqlite3_open(filename, &db);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_exec(db, CREATE_TABLE_ENTRYS, NULL, 0, &zErrMsg);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_exec(db, CREATE_TABLE_ENTRY_DATA, NULL, 0, &zErrMsg);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare(db, INSERT_ENTRY, -1, &insertEntryStmt, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare(db, INSERT_ENTRY_DATA, -1, &insertEntryDataStmt, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, GET_ENTRIES_WITH_RANGE, -1, &getEntriesWithRange, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, GET_ENTRIES_AFTER, -1, &getEntriesAfter, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, GET_ENTRY_DATA, -1, &getEntryData, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, GET_OLD_ENTRY, -1, &getOldEntry, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, GET_NEW_ENTRY, -1, &getNewEntry, NULL);
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
LogStorage self = (LogStorage) calloc(1, sizeof(struct sLogStorage));
|
||||
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) calloc(1, sizeof(struct sSqliteLogStorage));
|
||||
|
||||
instanceData->filename = copyStringInternal(filename);
|
||||
instanceData->db = db;
|
||||
instanceData->insertEntryStmt = insertEntryStmt;
|
||||
instanceData->insertEntryDataStmt = insertEntryDataStmt;
|
||||
instanceData->getEntriesWithRange = getEntriesWithRange;
|
||||
instanceData->getEntriesAfter = getEntriesAfter;
|
||||
instanceData->getEntryData = getEntryData;
|
||||
instanceData->getOldEntry = getOldEntry;
|
||||
instanceData->getNewEntry = getNewEntry;
|
||||
|
||||
self->instanceData = (void*) instanceData;
|
||||
|
||||
self->addEntry = SqliteLogStorage_addEntry;
|
||||
self->addEntryData = SqliteLogStorage_addEntryData;
|
||||
self->getEntries = SqliteLogStorage_getEntries;
|
||||
self->getEntriesAfter = SqliteLogStorage_getEntriesAfter;
|
||||
self->getOldestAndNewestEntries = SqliteLogStorage_getOldestAndNewestEntries;
|
||||
self->destroy = SqliteLogStorage_destroy;
|
||||
|
||||
return self;
|
||||
|
||||
exit_with_error:
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - failed to create LogStorage instance!\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp)
|
||||
{
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - add entry\n");
|
||||
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||||
|
||||
sqlite3* db = instanceData->db;
|
||||
int rc;
|
||||
char *zErrMsg = 0;
|
||||
|
||||
rc = sqlite3_bind_int64(instanceData->insertEntryStmt, 1, (sqlite_int64) timestamp);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_step(instanceData->insertEntryStmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
goto exit_with_error;
|
||||
|
||||
uint64_t id = sqlite3_last_insert_rowid(db);
|
||||
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - new entry with ID = %lu\n", id);
|
||||
|
||||
rc = sqlite3_reset(instanceData->insertEntryStmt);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
return id;
|
||||
|
||||
exit_with_error:
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - failed to add entry to log!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode)
|
||||
{
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||||
|
||||
sqlite3* db = instanceData->db;
|
||||
|
||||
int rc;
|
||||
|
||||
char *zErrMsg = 0;
|
||||
|
||||
rc = sqlite3_bind_int64(instanceData->insertEntryDataStmt, 1, (sqlite_int64) entryID);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_bind_text(instanceData->insertEntryDataStmt, 2, dataRef, -1, SQLITE_STATIC);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_bind_blob(instanceData->insertEntryDataStmt, 3, data, dataSize, SQLITE_STATIC);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_bind_int(instanceData->insertEntryDataStmt, 4, reasonCode);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_step(instanceData->insertEntryDataStmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
goto exit_with_error;
|
||||
|
||||
rc = sqlite3_reset(instanceData->insertEntryDataStmt);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
goto exit_with_error;
|
||||
|
||||
return true;
|
||||
|
||||
exit_with_error:
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - failed to add entry data!\n");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
getEntryData(LogStorage self, uint64_t entryID, LogEntryDataCallback entryDataCallback, void* parameter)
|
||||
{
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||||
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_bind_int64(instanceData->getEntryData, 1, entryID);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - getEntryData 1 rc:%i\n", rc);
|
||||
}
|
||||
|
||||
bool sendFinalEvent = true;
|
||||
|
||||
while ((rc = sqlite3_step(instanceData->getEntryData)) == SQLITE_ROW) {
|
||||
|
||||
const char* dataRef = (const char*) sqlite3_column_text(instanceData->getEntryData, 0);
|
||||
uint8_t* data = (uint8_t*) sqlite3_column_blob(instanceData->getEntryData, 1);
|
||||
int dataSize = sqlite3_column_bytes(instanceData->getEntryData, 1);
|
||||
int reasonCode = sqlite3_column_int(instanceData->getEntryData, 2);
|
||||
|
||||
if (entryDataCallback != NULL) {
|
||||
if (entryDataCallback(parameter, dataRef, data, dataSize, (uint8_t) reasonCode, true) == false) {
|
||||
sendFinalEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (sendFinalEvent) {
|
||||
if (entryDataCallback != NULL)
|
||||
entryDataCallback(parameter, NULL, NULL, 0, (uint8_t) 0, false);
|
||||
}
|
||||
|
||||
rc = sqlite3_reset(instanceData->getEntryData);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - getEntryData reset rc:%i\n", rc);
|
||||
}
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter)
|
||||
{
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||||
|
||||
sqlite3* db = instanceData->db;
|
||||
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 1, startingTime);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - SqliteLogStorage_getEntries 1 rc:%i\n", rc);
|
||||
|
||||
rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 2, endingTime);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - SqliteLogStorage_getEntries 2 rc:%i\n", rc);
|
||||
|
||||
bool sendFinalEvent = true;
|
||||
|
||||
while((rc = sqlite3_step(instanceData->getEntriesWithRange)) == SQLITE_ROW) {
|
||||
|
||||
uint64_t entryID = sqlite3_column_int64(instanceData->getEntriesWithRange, 0);
|
||||
uint64_t timestamp = sqlite3_column_int64(instanceData->getEntriesWithRange, 1);
|
||||
|
||||
if (entryCallback != NULL) {
|
||||
if (entryCallback(parameter, timestamp, entryID, true) == false) {
|
||||
sendFinalEvent = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getEntryData(self, entryID, entryDataCallback, parameter);
|
||||
}
|
||||
|
||||
if (sendFinalEvent)
|
||||
if (entryCallback != NULL)
|
||||
entryCallback(parameter, 0, 0, false);
|
||||
|
||||
|
||||
rc = sqlite3_reset(instanceData->getEntriesWithRange);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - SqliteLogStorage_getEntries reset rc:%i\n", rc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
|
||||
uint64_t* oldEntry, uint64_t* oldEntryTime)
|
||||
{
|
||||
bool validOldEntry = false;
|
||||
bool validNewEntry = false;
|
||||
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||||
|
||||
sqlite3* db = instanceData->db;
|
||||
|
||||
int rc;
|
||||
|
||||
/* Get oldest entry */
|
||||
|
||||
rc = sqlite3_step(instanceData->getOldEntry);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
*oldEntry = sqlite3_column_int64(instanceData->getOldEntry, 0);
|
||||
*oldEntryTime = sqlite3_column_int64(instanceData->getOldEntry, 1);
|
||||
validNewEntry = true;
|
||||
}
|
||||
else {
|
||||
*oldEntry = 0;
|
||||
*oldEntryTime = 0;
|
||||
}
|
||||
|
||||
sqlite3_reset(instanceData->getOldEntry);
|
||||
|
||||
/* Get newest entry */
|
||||
|
||||
rc = sqlite3_step(instanceData->getNewEntry);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
*newEntry = sqlite3_column_int64(instanceData->getNewEntry, 0);
|
||||
*newEntryTime = sqlite3_column_int64(instanceData->getNewEntry, 1);
|
||||
validOldEntry = true;
|
||||
}
|
||||
else {
|
||||
*newEntry = 0;
|
||||
*newEntryTime = 0;
|
||||
}
|
||||
|
||||
sqlite3_reset(instanceData->getNewEntry);
|
||||
|
||||
return (validOldEntry && validNewEntry);
|
||||
}
|
||||
|
||||
static bool
|
||||
SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter)
|
||||
{
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||||
|
||||
sqlite3* db = instanceData->db;
|
||||
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_bind_int64(instanceData->getEntriesAfter, 1, entryID);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - SqliteLogStorage_getEntriesAfter 1 rc:%i\n", rc);
|
||||
|
||||
bool sendFinalEvent = true;
|
||||
|
||||
while ((rc = sqlite3_step(instanceData->getEntriesAfter)) == SQLITE_ROW) {
|
||||
|
||||
uint64_t entryID = sqlite3_column_int64(instanceData->getEntriesAfter, 0);
|
||||
uint64_t timestamp = sqlite3_column_int64(instanceData->getEntriesAfter, 1);
|
||||
|
||||
if (entryCallback != NULL) {
|
||||
if (entryCallback(parameter, timestamp, entryID, true) == false) {
|
||||
sendFinalEvent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getEntryData(self, entryID, entryDataCallback, parameter);
|
||||
}
|
||||
|
||||
if (sendFinalEvent)
|
||||
if (entryCallback != NULL)
|
||||
entryCallback(parameter, 0, 0, false);
|
||||
|
||||
rc = sqlite3_reset(instanceData->getEntriesAfter);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - SqliteLogStorage_getEntriesAfter reset rc:%i\n", rc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
SqliteLogStorage_destroy(LogStorage self)
|
||||
{
|
||||
SqliteLogStorage* instanceData = (SqliteLogStorage*) self->instanceData;
|
||||
|
||||
sqlite3_finalize(instanceData->insertEntryStmt);
|
||||
sqlite3_finalize(instanceData->insertEntryDataStmt);
|
||||
sqlite3_finalize(instanceData->getEntriesWithRange);
|
||||
sqlite3_finalize(instanceData->getEntriesAfter);
|
||||
sqlite3_finalize(instanceData->getEntryData);
|
||||
sqlite3_finalize(instanceData->getOldEntry);
|
||||
sqlite3_finalize(instanceData->getNewEntry);
|
||||
|
||||
if (sqlite3_close(instanceData->db) != SQLITE_OK)
|
||||
if (DEBUG_LOG_STORAGE_DRIVER)
|
||||
printf("LOG_STORAGE_DRIVER: sqlite - SqliteLogStorage: failed to close database %s!\n", instanceData->filename);
|
||||
|
||||
free(instanceData->filename);
|
||||
free(instanceData);
|
||||
free(self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* log_storage.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "logging_api.h"
|
||||
|
||||
uint64_t
|
||||
LogStorage_addEntry(LogStorage self, uint64_t timestamp)
|
||||
{
|
||||
return self->addEntry(self, timestamp);
|
||||
}
|
||||
|
||||
bool
|
||||
LogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode)
|
||||
{
|
||||
return self->addEntryData(self, entryID, dataRef, data, dataSize, reasonCode);
|
||||
}
|
||||
|
||||
bool
|
||||
LogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter)
|
||||
{
|
||||
return self->getEntries(self, startingTime, endingTime, entryCallback, entryDataCallback, parameter);
|
||||
}
|
||||
|
||||
bool
|
||||
LogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter)
|
||||
{
|
||||
return self->getEntriesAfter(self, startingTime, entryID, entryCallback, entryDataCallback, parameter);
|
||||
}
|
||||
|
||||
void
|
||||
LogStorage_destroy(LogStorage self)
|
||||
{
|
||||
self->destroy(self);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
|
||||
uint64_t* oldEntry, uint64_t* oldEntryTime)
|
||||
{
|
||||
return self->getOldestAndNewestEntries(self, newEntry, newEntryTime, oldEntry, oldEntryTime);
|
||||
}
|
||||
|
||||
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* logging_api.h
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#ifndef LIBIEC61850_SRC_LOGGING_LOGGING_API_H_
|
||||
#define LIBIEC61850_SRC_LOGGING_LOGGING_API_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/** \addtogroup server_api_group
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup LOGGING_SPI Service provider interface (SPI) for log storage implementations
|
||||
*
|
||||
* This interface has to be implemented by the log storage provider. The Log storage provider
|
||||
* has to provide a specific constructor that creates an instance of LogStorage by allocating
|
||||
* the required memory fot the struct sLogStorage data structure and populate the function
|
||||
* pointers with provider specific implementation functions.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** The LogStorage object handle */
|
||||
typedef struct sLogStorage* LogStorage;
|
||||
|
||||
/**
|
||||
* \brief Will be called for each new LogEntry by the getEntries and getEntriesAfter functions
|
||||
*
|
||||
* \param parameter - a user provided parameter that is passed to the callback handler
|
||||
* \param timestamp - the entry timestamp of the LogEntry
|
||||
* \param entryID - the entryID of the LogEntry
|
||||
* \param moreFollow - more data will follow - if false, the data is not valid and no more data will follow
|
||||
*
|
||||
* \return true ready to receive new data, false abort operation
|
||||
*/
|
||||
typedef bool (*LogEntryCallback) (void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFollow);
|
||||
|
||||
/**
|
||||
* \brief Will be called for each new LogEntryData by the getEntries and getEntriesAfter functions
|
||||
*
|
||||
* \param parameter - a user provided parameter that is passed to the callback handler
|
||||
* \param dataRef - the data reference of the LogEntryData
|
||||
* \param data - the data content as an unstructured binary data block
|
||||
* \param dataSize - the size of the binary data block
|
||||
* \param reasonCode - the reasonCode of the LogEntryData
|
||||
* \param moreFollow - more data will follow - if false, the data is not valid and no more data will follow
|
||||
*
|
||||
* \return true ready to receive new data, false abort operation
|
||||
*/
|
||||
typedef bool (*LogEntryDataCallback) (void* parameter, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow);
|
||||
|
||||
struct sLogStorage {
|
||||
|
||||
void* instanceData;
|
||||
|
||||
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,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||||
|
||||
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);
|
||||
|
||||
void (*destroy) (LogStorage self);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Add an entry to the log
|
||||
*
|
||||
* \param self the pointer of the LogStorage instance
|
||||
* \param timestamp the entry time of the new entry
|
||||
*
|
||||
* \return the entryID of the new entry
|
||||
*/
|
||||
uint64_t
|
||||
LogStorage_addEntry(LogStorage self, uint64_t timestamp);
|
||||
|
||||
/**
|
||||
* \brief Add new entry data to an existing log entry
|
||||
*
|
||||
* \param self the pointer of the LogStorage instance
|
||||
* \param entryID the ID of the log entry where the data will be added
|
||||
* \param dataRef the data reference of the log entry data
|
||||
* \param data the data content as an unstructured binary data block
|
||||
* \param dataSize - the size of the binary data block
|
||||
* \param reasonCode - the reasonCode of the LogEntryData
|
||||
*
|
||||
* \return true if the entry data was successfully added, false otherwise
|
||||
*/
|
||||
bool
|
||||
LogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
|
||||
|
||||
/**
|
||||
* \brief Get log entries specified by a time range
|
||||
*
|
||||
* \param self the pointer of the LogStorage instance
|
||||
* \param startingTime start time of the time range
|
||||
* \param endingTime end time of the time range
|
||||
* \param entryCallback callback function to be called for each new log entry
|
||||
* \param entryDataCallback callback function to be called for each new log entry data
|
||||
* \param parameter - a user provided parameter that is passed to the callback handler
|
||||
*
|
||||
* \return true if the request has been successful, false otherwise
|
||||
*/
|
||||
bool
|
||||
LogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||||
|
||||
/**
|
||||
* \brief Get log entries specified by a start log entry
|
||||
*
|
||||
* The request will return all log entries that where entered into the log after the
|
||||
* log entry specified by startingTime and entryID.
|
||||
*
|
||||
* \param self the pointer of the LogStorage instance
|
||||
* \param startingTime timestamp of the start log entry
|
||||
* \param entryID entryID of the start log entry
|
||||
* \param entryCallback callback function to be called for each new log entry
|
||||
* \param entryDataCallback callback function to be called for each new log entry data
|
||||
* \param parameter - a user provided parameter that is passed to the callback handler
|
||||
*
|
||||
* \return true if the request has been successful, false otherwise
|
||||
*/
|
||||
bool
|
||||
LogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
|
||||
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||||
|
||||
/**
|
||||
* \brief Get the entry time and entryID of the oldest and the newest log entries
|
||||
*
|
||||
* This function is used to update the log status information in the LCB
|
||||
*
|
||||
* \param self the pointer of the LogStorage instance
|
||||
* \param newEntry pointer to store the entryID of the newest entry
|
||||
* \param newEntryTime pointer to store the entry time of the newest entry
|
||||
* \param oldEntry pointer to store the entryID of the oldest entry
|
||||
* \param oldEntryTime pointer to store the entry time of the oldest entry
|
||||
*
|
||||
*/
|
||||
bool
|
||||
LogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
|
||||
uint64_t* oldEntry, uint64_t* oldEntryTime);
|
||||
|
||||
/**
|
||||
* \brief Destroy the LogStorage instance and free all related resources
|
||||
*
|
||||
* \param self the pointer of the LogStorage instance
|
||||
*/
|
||||
void
|
||||
LogStorage_destroy(LogStorage self);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBIEC61850_SRC_LOGGING_LOGGING_API_H_ */
|
||||
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* mms_client_journals.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
#include "stack_config.h"
|
||||
#include "mms_common.h"
|
||||
#include "mms_client_connection.h"
|
||||
#include "byte_buffer.h"
|
||||
|
||||
#include "mms_client_internal.h"
|
||||
#include "ber_encoder.h"
|
||||
#include "ber_decode.h"
|
||||
#include "conversions.h"
|
||||
#include "mms_value_internal.h"
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
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 */
|
||||
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:
|
||||
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, LinkedList* result)
|
||||
{
|
||||
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++];
|
||||
|
||||
if (moreFollows)
|
||||
*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 */
|
||||
if (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;
|
||||
}
|
||||
|
||||
*result = journalEntries;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
mmsClient_createReadJournalRequestWithTimeRange(uint32_t invokeId, ByteBuffer* request, const char* domainId, const char* itemId,
|
||||
MmsValue* startingTime, MmsValue* endingTime)
|
||||
{
|
||||
/* 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 startingTimeSize = 2 + startingTime->value.binaryTime.size;
|
||||
|
||||
uint32_t rangeStartSpecSize = 2 + startingTimeSize;
|
||||
|
||||
uint32_t endingTimeSize = 2 + endingTime->value.binaryTime.size;
|
||||
|
||||
uint32_t rangeEndSpecSize = 2 + endingTimeSize;
|
||||
|
||||
uint32_t journalReadContentSize = journalNameSize + rangeStartSpecSize + rangeEndSpecSize;
|
||||
|
||||
uint32_t journalReadSize = 1 + BerEncoder_determineLengthSize(journalReadContentSize) + (journalReadContentSize);
|
||||
|
||||
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);
|
||||
|
||||
/* encode journalName */
|
||||
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);
|
||||
|
||||
/* encode rangeStartSpecification|startingTime */
|
||||
bufPos = BerEncoder_encodeTL(0xa1, startingTimeSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeOctetString(0x80, startingTime->value.binaryTime.buf,
|
||||
startingTime->value.binaryTime.size, buffer, bufPos);
|
||||
|
||||
/* encode rangeStopSpecification|endingTime */
|
||||
bufPos = BerEncoder_encodeTL(0xa2, endingTimeSize, buffer, bufPos);
|
||||
bufPos = BerEncoder_encodeOctetString(0x80, endingTime->value.binaryTime.buf,
|
||||
endingTime->value.binaryTime.size, buffer, bufPos);
|
||||
|
||||
request->size = bufPos;
|
||||
}
|
||||
|
||||
void
|
||||
mmsClient_createReadJournalRequestStartAfter(uint32_t invokeId, ByteBuffer* request, const char* domainId, const char* itemId,
|
||||
MmsValue* timeSpecification, MmsValue* entrySpecification)
|
||||
{
|
||||
/* 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 timeSpecificationSize = 2 + timeSpecification->value.binaryTime.size;
|
||||
|
||||
uint32_t entrySpecificationSize = 2 + entrySpecification->value.octetString.size;
|
||||
|
||||
uint32_t entryToStartAfterContentSize = timeSpecificationSize + entrySpecificationSize;
|
||||
|
||||
uint32_t entryToStartAfterSize = 1 + BerEncoder_determineLengthSize(entryToStartAfterContentSize)
|
||||
+ entryToStartAfterContentSize;
|
||||
|
||||
uint32_t journalReadContentSize = journalNameSize + entryToStartAfterSize;
|
||||
|
||||
uint32_t journalReadSize = 1 + BerEncoder_determineLengthSize(journalReadContentSize) + (journalReadContentSize);
|
||||
|
||||
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);
|
||||
|
||||
/* encode journalName */
|
||||
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);
|
||||
|
||||
/* encode entryToStartAfter */
|
||||
bufPos = BerEncoder_encodeTL(0xa5, entryToStartAfterContentSize, buffer, bufPos);
|
||||
|
||||
/* encode entryToStartAfter|timeSpecification */
|
||||
bufPos = BerEncoder_encodeOctetString(0x80, timeSpecification->value.binaryTime.buf,
|
||||
timeSpecification->value.binaryTime.size, buffer, bufPos);
|
||||
|
||||
/* encode entryToStartAfter|entrySpecification */
|
||||
bufPos = BerEncoder_encodeOctetString(0x81, entrySpecification->value.octetString.buf,
|
||||
entrySpecification->value.octetString.size, buffer, bufPos);
|
||||
|
||||
request->size = bufPos;
|
||||
}
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* mms_journal.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
#include "mms_device_model.h"
|
||||
#include "mms_server_internal.h"
|
||||
|
||||
MmsJournal
|
||||
MmsJournal_create(const char* name)
|
||||
{
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: create new journal %s\n", name);
|
||||
|
||||
MmsJournal self = (MmsJournal) GLOBAL_MALLOC(sizeof(struct sMmsJournal));
|
||||
|
||||
self->name = copyString(name);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
MmsJournal_destroy(MmsJournal self)
|
||||
{
|
||||
GLOBAL_FREEMEM(self->name);
|
||||
GLOBAL_FREEMEM(self);
|
||||
}
|
||||
@ -0,0 +1,533 @@
|
||||
/*
|
||||
* mms_journal_service.c
|
||||
*
|
||||
* Copyright 2016 Michael Zillgith
|
||||
*
|
||||
* This file is part of libIEC61850.
|
||||
*
|
||||
* libIEC61850 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* libIEC61850 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* See COPYING file for the complete license text.
|
||||
*/
|
||||
|
||||
#include "libiec61850_platform_includes.h"
|
||||
#include "mms_server_internal.h"
|
||||
#include "mms_value_internal.h"
|
||||
|
||||
#if (MMS_JOURNAL_SERVICE == 1)
|
||||
|
||||
|
||||
typedef struct sJournalVariable* JournalVariable;
|
||||
|
||||
struct sJournalVariable {
|
||||
char* tag; /* UTF8(1..255) */
|
||||
int tagSize;
|
||||
|
||||
uint8_t* data;
|
||||
int dataSize;
|
||||
|
||||
JournalVariable next;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t* entryID;
|
||||
int entryIDSize;
|
||||
|
||||
uint64_t timestamp;
|
||||
|
||||
JournalVariable listOfVariables;
|
||||
} 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)
|
||||
{
|
||||
JournalEncoder encoder = (JournalEncoder) parameter;
|
||||
|
||||
if (moreFollow) {
|
||||
|
||||
if (encoder->moreFollows)
|
||||
return false;
|
||||
|
||||
encoder->currentEntryBufPos = encoder->bufPos;
|
||||
|
||||
encoder->bufPos += 48; /* reserve space for common entry parts */
|
||||
|
||||
encoder->currentEntryId = entryID;
|
||||
encoder->currentTimestamp = timestamp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* REASON_CODE_STR = "ReasonCode";
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
uint32_t firstVariableLen = 1 + BerEncoder_determineLengthSize(valueSpecLen + dataRefLen)
|
||||
+ valueSpecLen + dataRefLen;
|
||||
|
||||
uint8_t reasonCodeNBuf[2];
|
||||
MmsValue reasonCodeValue;
|
||||
reasonCodeValue.type = MMS_BIT_STRING;
|
||||
reasonCodeValue.value.bitString.size = 7;
|
||||
reasonCodeValue.value.bitString.buf = reasonCodeNBuf;
|
||||
|
||||
MmsValue_setBitStringFromInteger(&reasonCodeValue, reasonCode);
|
||||
|
||||
uint32_t reasonCodeValueLen = MmsValue_encodeMmsData(&reasonCodeValue, NULL, 0, false);
|
||||
|
||||
uint32_t reasonCodeContentLen = reasonCodeValueLen + 2 + 12;
|
||||
|
||||
uint32_t secondVariableLen = 1 + BerEncoder_determineLengthSize(reasonCodeContentLen)
|
||||
+ reasonCodeContentLen;
|
||||
|
||||
uint32_t totalLen = firstVariableLen + secondVariableLen;
|
||||
|
||||
if ((int) (bufPos + totalLen) > encoder->maxSize) {
|
||||
encoder->moreFollows = true;
|
||||
encoder->bufPos = encoder->currentEntryBufPos; /* remove last entry */
|
||||
return false;
|
||||
}
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0x30, valueSpecLen + dataRefLen, buffer, bufPos);
|
||||
|
||||
/* encode dataRef */
|
||||
bufPos = BerEncoder_encodeOctetString(0x80, (uint8_t*) dataRef, dataRefStrLen, buffer, bufPos);
|
||||
|
||||
/* encode valueSpec */
|
||||
bufPos = BerEncoder_encodeOctetString(0xa1, data, dataSize, buffer, bufPos);
|
||||
|
||||
/* encode reasonCode */
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0x30, reasonCodeContentLen , buffer, bufPos);
|
||||
|
||||
bufPos = BerEncoder_encodeOctetString(0x80, (uint8_t*) REASON_CODE_STR, 10, buffer, bufPos);
|
||||
|
||||
bufPos = BerEncoder_encodeTL(0xa1, reasonCodeValueLen, buffer, bufPos);
|
||||
bufPos = MmsValue_encodeMmsData(&reasonCodeValue, buffer, bufPos, true);
|
||||
|
||||
encoder->bufPos = bufPos;
|
||||
}
|
||||
else {
|
||||
int dataContentLen = encoder->bufPos - (encoder->currentEntryBufPos + 48);
|
||||
|
||||
int journalVariablesLen = 1 + BerEncoder_determineLengthSize(dataContentLen) + dataContentLen;
|
||||
|
||||
int dataLen = 1 + BerEncoder_determineLengthSize(journalVariablesLen) + journalVariablesLen;
|
||||
|
||||
int dataAndTimeLen = dataLen + 8;
|
||||
|
||||
int entryContentLen = 1 + BerEncoder_determineLengthSize(dataAndTimeLen) + dataAndTimeLen;
|
||||
|
||||
int journalEntryContentLen = 10 /* entryIdentifier */
|
||||
+ 4 /* originatingApplication */
|
||||
+ entryContentLen;
|
||||
|
||||
int headerBufPos = encoder->currentEntryBufPos;
|
||||
|
||||
headerBufPos = BerEncoder_encodeTL(0x30, journalEntryContentLen, buffer, headerBufPos);
|
||||
|
||||
headerBufPos = BerEncoder_encodeOctetString(0x80, (uint8_t*) &(encoder->currentEntryId), 8, buffer, headerBufPos);
|
||||
|
||||
headerBufPos = BerEncoder_encodeTL(0xa1, 2, buffer, headerBufPos);
|
||||
headerBufPos = BerEncoder_encodeTL(0x30, 0, buffer, headerBufPos);
|
||||
|
||||
headerBufPos = BerEncoder_encodeTL(0xa2, dataAndTimeLen, buffer, headerBufPos);
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t tag = buffer[(*bufPos)++];
|
||||
int length;
|
||||
|
||||
if (tag != 0x1a) {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
|
||||
return false;
|
||||
}
|
||||
|
||||
*bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos);
|
||||
|
||||
if (*bufPos < 0) {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length > maxLength) {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(filename, buffer + *bufPos, length);
|
||||
filename[length] = 0;
|
||||
*bufPos += length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define RESERVED_SPACE_FOR_HEADER 22
|
||||
|
||||
void
|
||||
mmsServer_handleReadJournalRequest(
|
||||
MmsServerConnection connection,
|
||||
uint8_t* requestBuffer,
|
||||
int bufPos, int maxBufPos,
|
||||
uint32_t invokeId,
|
||||
ByteBuffer* response)
|
||||
{
|
||||
char domainId[65];
|
||||
char logName[65];
|
||||
uint8_t entryIdBuf[64]; /* maximum size of entry id is 64 bytes! */
|
||||
|
||||
MmsValue entrySpec;
|
||||
entrySpec.type = MMS_OCTET_STRING;
|
||||
entrySpec.value.octetString.buf = entryIdBuf;
|
||||
entrySpec.value.octetString.maxSize = 64;
|
||||
|
||||
MmsValue rangeStart;
|
||||
MmsValue rangeStop;
|
||||
|
||||
bool hasNames = false;
|
||||
bool hasRangeStartSpec = false;
|
||||
bool hasRangeStopSpec = false;
|
||||
bool hasTimeSpec = false;
|
||||
bool hasEntrySpec = false;
|
||||
|
||||
while (bufPos < maxBufPos) {
|
||||
uint8_t tag = requestBuffer[bufPos++];
|
||||
int length;
|
||||
|
||||
bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos);
|
||||
|
||||
if (bufPos < 0) {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
|
||||
case 0xa0: /* journalName */
|
||||
{
|
||||
uint8_t objectIdTag = requestBuffer[bufPos++];
|
||||
bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos);
|
||||
|
||||
switch (objectIdTag) {
|
||||
case 0xa1: /* domain-specific */
|
||||
|
||||
if (!parseStringWithMaxLength(domainId, 64, requestBuffer, &bufPos, bufPos + length, invokeId, response)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parseStringWithMaxLength(logName, 64, requestBuffer, &bufPos, bufPos + length, invokeId, response)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasNames = true;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response);
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0xa1: /* rangeStartSpecification */
|
||||
{
|
||||
uint8_t subTag = requestBuffer[bufPos++];
|
||||
bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos);
|
||||
|
||||
if (subTag != 0x80) {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ((length == 4) || (length == 6)) {
|
||||
|
||||
rangeStart.type = MMS_BINARY_TIME;
|
||||
rangeStart.value.binaryTime.size = length;
|
||||
|
||||
memcpy(rangeStart.value.binaryTime.buf, requestBuffer + bufPos, length);
|
||||
|
||||
hasRangeStartSpec = true;
|
||||
}
|
||||
else {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return; // forward request to implementation class
|
||||
}
|
||||
|
||||
bufPos += length;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0xa2: /* rangeStopSpecification */
|
||||
{
|
||||
uint8_t subTag = requestBuffer[bufPos++];
|
||||
bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos);
|
||||
|
||||
if (subTag != 0x80) {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((length == 4) || (length == 6)) {
|
||||
rangeStop.type = MMS_BINARY_TIME;
|
||||
rangeStop.value.binaryTime.size = length;
|
||||
|
||||
memcpy(rangeStop.value.binaryTime.buf, requestBuffer + bufPos, length);
|
||||
|
||||
hasRangeStopSpec = true;
|
||||
}
|
||||
else {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return; // forward request to implementation class
|
||||
}
|
||||
|
||||
bufPos += length;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0xa5: /* entryToStartAfter */
|
||||
{
|
||||
int maxSubBufPos = bufPos + length;
|
||||
|
||||
while (bufPos < maxSubBufPos) {
|
||||
uint8_t subTag = requestBuffer[bufPos++];
|
||||
bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos);
|
||||
|
||||
switch (subTag) {
|
||||
case 0x80: /* timeSpecification */
|
||||
|
||||
if ((length == 4) || (length == 6)) {
|
||||
rangeStart.type = MMS_BINARY_TIME;
|
||||
rangeStart.value.binaryTime.size = length;
|
||||
|
||||
memcpy(rangeStart.value.binaryTime.buf, requestBuffer + bufPos, length);
|
||||
|
||||
hasTimeSpec = true;
|
||||
}
|
||||
else {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x81: /* entrySpecification */
|
||||
|
||||
if (length <= entrySpec.value.octetString.maxSize) {
|
||||
memcpy(entrySpec.value.octetString.buf, requestBuffer + bufPos, length);
|
||||
entrySpec.value.octetString.size = length;
|
||||
|
||||
hasEntrySpec = true;
|
||||
}
|
||||
else {
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response);
|
||||
return;
|
||||
}
|
||||
|
||||
bufPos += length;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO check if required fields are present
|
||||
if (hasNames == false) {
|
||||
if (DEBUG_MMS_SERVER)
|
||||
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 */
|
||||
MmsDevice* mmsDevice = MmsServer_getDevice(connection->server);
|
||||
|
||||
MmsDomain* mmsDomain = MmsDevice_getDomain(mmsDevice, domainId);
|
||||
|
||||
if (mmsDomain == NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
MmsJournal mmsJournal = MmsDomain_getJournal(mmsDomain, logName);
|
||||
|
||||
if (mmsJournal == NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal - 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 {
|
||||
if (DEBUG_MMS_SERVER)
|
||||
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 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 */
|
||||
|
||||
if (DEBUG_MMS_SERVER)
|
||||
printf("MMS_SERVER: readJournal: 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) */
|
||||
@ -0,0 +1,20 @@
|
||||
1 VERSIONINFO
|
||||
FILEVERSION @LIB_VERSION_MAJOR@,@LIB_VERSION_MINOR@,@LIB_VERSION_PATCH@,0
|
||||
PRODUCTVERSION @LIB_VERSION_MAJOR@,@LIB_VERSION_MINOR@,@LIB_VERSION_PATCH@,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "FileVersion", "@LIB_VERSION_MAJOR@.@LIB_VERSION_MINOR@.@LIB_VERSION_PATCH@.0"
|
||||
VALUE "ProductVersion", "@LIB_VERSION_MAJOR@.@LIB_VERSION_MINOR@.@LIB_VERSION_PATCH@.0"
|
||||
VALUE "ProductName", "libIEC61850"
|
||||
VALUE "FileDescription", "libIEC61850 - open source library for IEC 61850"
|
||||
VALUE "LegalCopyright", "Dual license : Commercial or GPLv3"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x400, 1252
|
||||
END
|
||||
END
|
||||
@ -0,0 +1,48 @@
|
||||
# - Try to find the sqlite library
|
||||
# Once done this will define
|
||||
#
|
||||
# SQLITE_FOUND - system has sqlite
|
||||
# SQLITE_INCLUDE_DIRS - the sqlite include directory
|
||||
# SQLITE_LIBRARIES - Link these to use sqlite
|
||||
#
|
||||
# Define SQLITE_MIN_VERSION for which version desired.
|
||||
#
|
||||
|
||||
INCLUDE(FindPkgConfig)
|
||||
|
||||
IF(Sqlite_FIND_REQUIRED)
|
||||
SET(_pkgconfig_REQUIRED "REQUIRED")
|
||||
ELSE(Sqlite_FIND_REQUIRED)
|
||||
SET(_pkgconfig_REQUIRED "")
|
||||
ENDIF(Sqlite_FIND_REQUIRED)
|
||||
|
||||
IF(SQLITE_MIN_VERSION)
|
||||
PKG_SEARCH_MODULE(SQLITE ${_pkgconfig_REQUIRED} sqlite>=${SQLITE_MIN_VERSION} sqlite${SQLITE_MIN_VERSION})
|
||||
ELSE(SQLITE_MIN_VERSION)
|
||||
PKG_SEARCH_MODULE(SQLITE ${_pkgconfig_REQUIRED} sqlite)
|
||||
ENDIF(SQLITE_MIN_VERSION)
|
||||
|
||||
IF(NOT SQLITE_FOUND AND NOT PKG_CONFIG_FOUND)
|
||||
FIND_PATH(SQLITE_INCLUDE_DIRS sqlite${SQLITE_MIN_VERSION}.h)
|
||||
FIND_LIBRARY(SQLITE_LIBRARIES sqlite${SQLITE_MIN_VERSION})
|
||||
|
||||
# Report results
|
||||
IF(SQLITE_LIBRARIES AND SQLITE_INCLUDE_DIRS)
|
||||
SET(SQLITE_FOUND 1)
|
||||
IF(NOT Sqlite_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Found Sqlite: ${SQLITE_LIBRARIES}")
|
||||
ENDIF(NOT Sqlite_FIND_QUIETLY)
|
||||
ELSE(SQLITE_LIBRARIES AND SQLITE_INCLUDE_DIRS)
|
||||
IF(Sqlite_FIND_REQUIRED)
|
||||
MESSAGE(SEND_ERROR "Could not find Sqlite")
|
||||
ELSE(Sqlite_FIND_REQUIRED)
|
||||
IF(NOT Sqlite_FIND_QUIETLY)
|
||||
MESSAGE(STATUS "Could not find Sqlite")
|
||||
ENDIF(NOT Sqlite_FIND_QUIETLY)
|
||||
ENDIF(Sqlite_FIND_REQUIRED)
|
||||
ENDIF(SQLITE_LIBRARIES AND SQLITE_INCLUDE_DIRS)
|
||||
ENDIF(NOT SQLITE_FOUND AND NOT PKG_CONFIG_FOUND)
|
||||
|
||||
# Hide advanced variables from CMake GUIs
|
||||
MARK_AS_ADVANCED(SQLITE_LIBRARIES SQLITE_INCLUDE_DIRS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue