Skip to content

Commit 627a053

Browse files
feat: describe LogControl
1 parent fcfbc2c commit 627a053

File tree

5 files changed

+161
-0
lines changed

5 files changed

+161
-0
lines changed

describe/LN.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ const scl = new DOMParser().parseFromString(
3535
</SDI>
3636
</SDI>
3737
</DOI>
38+
<LogControl name="anotherLog" logName="logName" />
39+
<LogControl name="log" dataSet="baseDataSet" logName="logName" reasonCode="true" logEna="true" intgPd="0" bufTime="0" >
40+
<TrgOps dchg="false" qchg="false" dupd="false" period="false" gi="false" />
41+
</LogControl>
3842
<ReportControl name="report" datSet="baseDataSet" intgPd="0" indexed="true" buffered="true" bufTime="0" confRev="0" >
3943
<TrgOps dchg="false" qchg="false" dupd="false" period="false" gi="false" />
4044
<OptFields seqNum="false" timeStamp="false" dataSet="false" reasonCode="false" dataRef="false" entryID="false" configRef="false" bufOvfl="false"/>
@@ -55,6 +59,8 @@ const scl = new DOMParser().parseFromString(
5559
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="LLN0" lnInst="" doName="Beh" daName="stVal" fc="ST" />
5660
<FCDA iedName="IED1" ldInst="lDevice" lnClass="LLN0" doName="Beh" fc="ST" />
5761
</DataSet>
62+
<LogControl name="log" dataSet="equalDataSet" logName="logName" />
63+
<LogControl name="anotherLog" logName="logName" />
5864
<ReportControl name="anotherReport" />
5965
<ReportControl name="report" datSet="equalDataSet" />
6066
</LN>

describe/LN.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
LNodeTypeDescription,
99
isLNodeTypeDescription,
1010
} from "./LNodeType.js";
11+
import { LogControlDescription, describeLogControl } from "./LogControl.js";
1112
import { NamingDescription, describeNaming } from "./Naming.js";
1213
import {
1314
ReportControlDescription,
@@ -17,6 +18,7 @@ import { describeVal, compareBySGroup } from "./Val.js";
1718

1819
export interface LNDescription extends NamingDescription {
1920
reports: Record<string, ReportControlDescription>;
21+
logControls: Record<string, LogControlDescription>;
2022
lnType: LNodeTypeDescription;
2123
}
2224

@@ -68,6 +70,24 @@ function getNextDataType(
6870
else return dataType.bdas[path[index]];
6971
}
7072

73+
function logControls(element: Element): Record<string, LogControlDescription> {
74+
const unsortedLogControls: Record<string, LogControlDescription> = {};
75+
76+
Array.from(element.children)
77+
.filter((child) => child.tagName === "LogControl")
78+
.forEach((logControl) => {
79+
const name = logControl.getAttribute("name");
80+
const logControlDescription = describeLogControl(logControl);
81+
if (name && !unsortedLogControls[name] && logControlDescription)
82+
unsortedLogControls[name] = logControlDescription;
83+
});
84+
85+
return sortRecord(unsortedLogControls) as Record<
86+
string,
87+
LogControlDescription
88+
>;
89+
}
90+
7191
/** Returns leaf data attribute (BDA or DA) from
7292
* LNodeTypeDescription containing vals
7393
* @param path - parent DOI/SDI/DAI name attributes
@@ -138,5 +158,6 @@ export function LN(element: Element): LNDescription | undefined {
138158
...describeNaming(element),
139159
lnType,
140160
reports: reportControls(element),
161+
logControls: logControls(element),
141162
};
142163
}

describe/LogControl.spec.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { expect } from "chai";
2+
import { describeLogControl } from "./LogControl.js";
3+
4+
const scl = new DOMParser().parseFromString(
5+
`<SCL xmlns="http://www.iec.ch/61850/2003/SCL" >
6+
<IED name="IED1">
7+
<AccessPoint name="AP1">
8+
<Server>
9+
<LDevice inst="lDevice">
10+
<LN0 lnClass="LLN0" inst="" lnType="LLN0">
11+
<DataSet name="baseDataSet" >
12+
<FCDA iedName="IED1" ldInst="lDevice" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
13+
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="q" fc="ST" />
14+
<FCDA iedName="IED1" ldInst="lDevice" lnClass="LLN0" doName="Beh" daName="stVal" fc="ST" />
15+
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="LLN0" lnInst="" doName="Beh" fc="ST" />
16+
</DataSet>
17+
<DataSet name="equalDataSet" >
18+
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
19+
<FCDA iedName="IED1" ldInst="lDevice" lnClass="XCBR" lnInst="1" doName="Pos" daName="q" fc="ST" />
20+
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="LLN0" lnInst="" doName="Beh" daName="stVal" fc="ST" />
21+
<FCDA iedName="IED1" ldInst="lDevice" lnClass="LLN0" doName="Beh" fc="ST" />
22+
</DataSet>
23+
<DataSet name="diffDataSet" >
24+
<Private type="private" />
25+
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
26+
<FCDA iedName="IED1" ldInst="lDevice" lnClass="XCBR" lnInst="1" doName="Pos" daName="q" fc="ST" />
27+
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="LLN0" lnInst="" doName="Beh" daName="stVal" fc="ST" />
28+
<FCDA iedName="IED1" ldInst="lDevice" lnClass="LLN0" doName="Beh" fc="ST" />
29+
</DataSet>
30+
<DataSet name="invalidDataSet" >
31+
<FCDA ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
32+
</DataSet>
33+
<DataSet name="invalidDataSet" >
34+
<FCDA ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
35+
</DataSet>
36+
<LogControl name="baseLogControl" datSet="baseDataSet" ldInst="lDevice" prefix="" lnClass="LLN0" logName="logName" logEna="true" bufTime="0" >
37+
<TrgOps dchg="false" qchg="false" dupd="false" period="false" gi="false" />
38+
</LogControl>
39+
<LogControl name="diffLogControl" datSet="diffDataSet" intgPd="1000" reasonCode="false" logEna="false" logName="logName" >
40+
<TrgOps dchg="false" qchg="true" dupd="false" period="true" gi="false" />
41+
</LogControl>
42+
<LogControl name="equalLogControl" datSet="equalDataSet" ldInst="lDevice" logName="logName" reasonCode="true" />
43+
<LogControl name="missingLogName" datSet="diffDataSet" />
44+
<LogControl name="invalidLogControl" datSet="invalidDataSet" />
45+
</LN0>
46+
</LDevice>
47+
</Server>
48+
</AccessPoint>
49+
</IED>
50+
</SCL>`,
51+
"application/xml",
52+
);
53+
54+
const baseLogControl = scl.querySelector(`LogControl[name="baseLogControl"]`)!;
55+
const equalLogControl = scl.querySelector(
56+
'LogControl[name="equalLogControl"]',
57+
)!;
58+
const diffLogControl = scl.querySelector('LogControl[name="diffLogControl"]')!;
59+
const invalidLogControl = scl.querySelector(
60+
'LogControl[name="invalidLogControl"]',
61+
)!;
62+
const missingLogName = scl.querySelector('LogControl[name="missingLogName"]')!;
63+
64+
describe("Description for SCL schema type LogControl", () => {
65+
it("returns undefined with invalid data", () =>
66+
expect(describeLogControl(invalidLogControl)).to.be.undefined);
67+
68+
it("returns undefined with invalid logName attribute", () =>
69+
expect(describeLogControl(missingLogName)).to.be.undefined);
70+
71+
it("returns same description with semantically equal Control's", () =>
72+
expect(JSON.stringify(describeLogControl(baseLogControl))).to.equal(
73+
JSON.stringify(describeLogControl(equalLogControl)),
74+
));
75+
76+
it("returns different description with unequal Control elements", () =>
77+
expect(JSON.stringify(describeLogControl(baseLogControl))).to.not.equal(
78+
JSON.stringify(describeLogControl(diffLogControl)),
79+
));
80+
});

describe/LogControl.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
ControlWithTriggerOptDescription,
3+
describeControlWithTriggerOpt,
4+
} from "./ControlWithTriggerOpt.js";
5+
6+
export interface LogControlDescription
7+
extends ControlWithTriggerOptDescription {
8+
/** ReportControl attribute ldInst. */
9+
ldInst: string | null;
10+
/** LogControl attribute prefix defaulted to "". */
11+
prefix: string;
12+
/** LogControl attribute lnClass defaulted to "LLN0". */
13+
lnClass: string;
14+
/** LogControl attribute lnInst. */
15+
lnInst: string | null;
16+
/** LogControl attribute logName. Must point to valid `Log` element */
17+
logName: string;
18+
/** LogControl attribute logEna defaulted to true. */
19+
logEna: boolean;
20+
/** LogControl attribute reasonCode defaulted to true. */
21+
reasonCode: boolean;
22+
/** LogCOntrol attribute bufTime defaulted to "0". */
23+
bufTime: number;
24+
}
25+
26+
export function describeLogControl(
27+
element: Element,
28+
): LogControlDescription | undefined {
29+
const controlWithTriggerOptDesc = describeControlWithTriggerOpt(element);
30+
if (!controlWithTriggerOptDesc) return;
31+
32+
const logName = element.getAttribute("logName");
33+
if (!logName) return;
34+
35+
const logControlDescription: LogControlDescription = {
36+
...controlWithTriggerOptDesc,
37+
ldInst: element.getAttribute("ldInst"),
38+
prefix: element.getAttribute("prefix") ?? "",
39+
lnClass: element.getAttribute("lnClass") ?? "LLN0",
40+
lnInst: element.getAttribute("lnInst"),
41+
logEna: element.getAttribute("logEna") === "false" ? false : true,
42+
reasonCode: element.getAttribute("reasonCode") === "false" ? false : true,
43+
bufTime: 0,
44+
logName,
45+
};
46+
47+
const bufTime = element.getAttribute("bufTime");
48+
if (bufTime && !isNaN(parseInt(bufTime, 10)))
49+
logControlDescription.bufTime = parseInt(bufTime, 10);
50+
51+
return logControlDescription;
52+
}

utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { DADescription } from "./describe/DADescription.js";
22
import { DODescription } from "./describe/DODescription.js";
3+
import { LogControlDescription } from "./describe/LogControl.js";
34
import { ReportControlDescription } from "./describe/ReportControl.js";
45
import { SDODescription } from "./describe/SDODescription.js";
56

67
type SortedObjects =
78
| DADescription
9+
| LogControlDescription
810
| SDODescription
911
| ReportControlDescription
1012
| DODescription;

0 commit comments

Comments
 (0)