@open-charging-cloud/chargy-core 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/ACrypt.d.ts +2 -2
- package/dist/ACrypt.d.ts.map +1 -1
- package/dist/Alfen.d.ts +6 -6
- package/dist/Alfen.d.ts.map +1 -1
- package/dist/BSMCrypt01.d.ts +8 -9
- package/dist/BSMCrypt01.d.ts.map +1 -1
- package/dist/EMHCrypt01.d.ts +8 -8
- package/dist/EMHCrypt01.d.ts.map +1 -1
- package/dist/GDFCrypt01.d.ts +8 -8
- package/dist/GDFCrypt01.d.ts.map +1 -1
- package/dist/Mennekes.d.ts +22 -22
- package/dist/Mennekes.d.ts.map +1 -1
- package/dist/OCMF.d.ts +25 -27
- package/dist/OCMF.d.ts.map +1 -1
- package/dist/OCPI.d.ts +2 -4
- package/dist/OCPI.d.ts.map +1 -1
- package/dist/PCDF.d.ts +1 -1
- package/dist/PCDF.d.ts.map +1 -1
- package/dist/QIDigital_DCC.d.ts +1 -1
- package/dist/QIDigital_DCC.d.ts.map +1 -1
- package/dist/QIDigital_DCoA.d.ts +1 -1
- package/dist/QIDigital_DCoA.d.ts.map +1 -1
- package/dist/QIDigital_DCoC.d.ts +1 -1
- package/dist/QIDigital_DCoC.d.ts.map +1 -1
- package/dist/SAFE_XML.d.ts +16 -16
- package/dist/SAFE_XML.d.ts.map +1 -1
- package/dist/XMLContainer.d.ts +2 -2
- package/dist/XMLContainer.d.ts.map +1 -1
- package/dist/browser/index.js +943 -895
- package/dist/browser/index.js.map +1 -1
- package/dist/chargeIT.d.ts +1 -3
- package/dist/chargeIT.d.ts.map +1 -1
- package/dist/chargePoint.d.ts +3 -3
- package/dist/chargePoint.d.ts.map +1 -1
- package/dist/chargy.d.ts +12 -12
- package/dist/chargy.d.ts.map +1 -1
- package/dist/chargyLib.d.ts +4 -0
- package/dist/chargyLib.d.ts.map +1 -1
- package/dist/interfaces/IChargeTransparencyRecord.d.ts +76 -76
- package/dist/interfaces/IChargeTransparencyRecord.d.ts.map +1 -1
- package/dist/interfaces/chargyInterfaces.d.ts +97 -107
- package/dist/interfaces/chargyInterfaces.d.ts.map +1 -1
- package/dist/node/index.js +943 -895
- package/dist/node/index.js.map +1 -1
- package/dist/pdfjs/browser.d.ts +2 -1
- package/dist/pdfjs/browser.d.ts.map +1 -1
- package/dist/qrCodeReader.d.ts.map +1 -1
- package/dist/secp224k1.d.ts +10 -11
- package/dist/secp224k1.d.ts.map +1 -1
- package/dist/verificationResults.d.ts +1 -1
- package/dist/verificationResults.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/browser/index.js
CHANGED
|
@@ -9768,9 +9768,7 @@ var secp224k1 = class {
|
|
|
9768
9768
|
N = BigInt("0x010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7");
|
|
9769
9769
|
// Number of points in the field
|
|
9770
9770
|
Acurve = BigInt(0);
|
|
9771
|
-
//
|
|
9772
|
-
Bcurve = BigInt(5);
|
|
9773
|
-
// These two are defined on the elliptic curve. y^2 = x^3 + Acurve * x + Bcurve
|
|
9771
|
+
// Defined on the elliptic curve. y^2 = x^3 + Acurve * x + 5
|
|
9774
9772
|
Gx = BigInt("0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C");
|
|
9775
9773
|
Gy = BigInt("0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5");
|
|
9776
9774
|
GPoint = [this.Gx, this.Gy];
|
|
@@ -9867,6 +9865,7 @@ var secp224k1 = class {
|
|
|
9867
9865
|
address: this.modulo(publicKey[1], this.Two) == this.One ? "03" + Px : "02" + Px
|
|
9868
9866
|
};
|
|
9869
9867
|
}
|
|
9868
|
+
return void 0;
|
|
9870
9869
|
}
|
|
9871
9870
|
};
|
|
9872
9871
|
|
|
@@ -9995,13 +9994,13 @@ function CreateError(message, level = "high" /* high */) {
|
|
|
9995
9994
|
};
|
|
9996
9995
|
}
|
|
9997
9996
|
function isISessionCryptoResult1(obj) {
|
|
9998
|
-
return
|
|
9997
|
+
return isObject(obj) && obj["status"] !== void 0;
|
|
9999
9998
|
}
|
|
10000
9999
|
function isISessionCryptoResult2(obj) {
|
|
10001
|
-
return
|
|
10000
|
+
return isObject(obj) && obj["status"] !== void 0 && obj["status"] !== "InvalidSessionFormat" /* InvalidSessionFormat */;
|
|
10002
10001
|
}
|
|
10003
10002
|
function isICryptoResult(obj) {
|
|
10004
|
-
return
|
|
10003
|
+
return isObject(obj) && obj["status"] !== void 0;
|
|
10005
10004
|
}
|
|
10006
10005
|
var InformationRelevance = /* @__PURE__ */ ((InformationRelevance2) => {
|
|
10007
10006
|
InformationRelevance2["Unknown"] = "Unknown";
|
|
@@ -10089,6 +10088,26 @@ function toArrayBuffer(data) {
|
|
|
10089
10088
|
function toUint8Array(data) {
|
|
10090
10089
|
return data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
10091
10090
|
}
|
|
10091
|
+
function getArrayElement(values, index, message = "Missing array element") {
|
|
10092
|
+
const value = values[index];
|
|
10093
|
+
if (value === void 0)
|
|
10094
|
+
throw new Error(message + " at index " + index.toString());
|
|
10095
|
+
return value;
|
|
10096
|
+
}
|
|
10097
|
+
function getFirstArrayElement(values, message = "Missing first array element") {
|
|
10098
|
+
return getArrayElement(values, 0, message);
|
|
10099
|
+
}
|
|
10100
|
+
function getLastArrayElement(values, message = "Missing last array element") {
|
|
10101
|
+
return getArrayElement(values, values.length - 1, message);
|
|
10102
|
+
}
|
|
10103
|
+
function getArrayLikeElement(values, index, message = "Missing array-like element") {
|
|
10104
|
+
if (index < 0 || index >= values.length)
|
|
10105
|
+
throw new Error(message + " at index " + index.toString());
|
|
10106
|
+
const value = values[index];
|
|
10107
|
+
if (value === void 0)
|
|
10108
|
+
throw new Error(message + " at index " + index.toString());
|
|
10109
|
+
return value;
|
|
10110
|
+
}
|
|
10092
10111
|
function normalizeXMLText(xmlText) {
|
|
10093
10112
|
return (xmlText ?? "").replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n").replace(/>\s+</g, "><").trim();
|
|
10094
10113
|
}
|
|
@@ -10100,15 +10119,16 @@ function getDirectChildByLocalName(parent, localName) {
|
|
|
10100
10119
|
}
|
|
10101
10120
|
function getTrimmedTextContent(element) {
|
|
10102
10121
|
const text = element?.textContent.trim();
|
|
10103
|
-
return text && text.length > 0 ? text : void 0;
|
|
10122
|
+
return text != null && text.length > 0 ? text : void 0;
|
|
10104
10123
|
}
|
|
10105
10124
|
function parseDescription(parent) {
|
|
10106
10125
|
const descriptions = getDirectChildrenByLocalName(parent, "description");
|
|
10107
10126
|
const textMap = {};
|
|
10108
10127
|
for (const description of descriptions) {
|
|
10109
|
-
const
|
|
10128
|
+
const languageAttribute = description.getAttribute("language")?.trim();
|
|
10129
|
+
const language = languageAttribute != null && languageAttribute.length > 0 ? languageAttribute : "und";
|
|
10110
10130
|
const text = description.textContent.trim();
|
|
10111
|
-
if (text)
|
|
10131
|
+
if (text.length > 0)
|
|
10112
10132
|
textMap[language] = text;
|
|
10113
10133
|
}
|
|
10114
10134
|
return Object.keys(textMap).length > 0 ? textMap : void 0;
|
|
@@ -10144,12 +10164,14 @@ function ParseJSON_LD(Text, _Context = "") {
|
|
|
10144
10164
|
function firstKey(obj) {
|
|
10145
10165
|
for (const a in obj)
|
|
10146
10166
|
return a;
|
|
10167
|
+
return void 0;
|
|
10147
10168
|
}
|
|
10148
10169
|
function firstValue(obj) {
|
|
10149
10170
|
if (obj == void 0)
|
|
10150
10171
|
return void 0;
|
|
10151
10172
|
for (const a in obj)
|
|
10152
10173
|
return obj[a];
|
|
10174
|
+
return void 0;
|
|
10153
10175
|
}
|
|
10154
10176
|
var uiLocale = typeof window !== "undefined" ? window.navigator.language : "de";
|
|
10155
10177
|
function setUILocale(locale) {
|
|
@@ -10182,14 +10204,17 @@ function parseOBIS(OBIS) {
|
|
|
10182
10204
|
return `${String(media)}-${String(channel)}:${String(indicator)}.${String(mode)}.${String(quantities)}*${String(storage)}`;
|
|
10183
10205
|
}
|
|
10184
10206
|
var OBIS_RegExpr = new RegExp("((\\d+)\\-)?((\\d+):)?((\\d+)\\.)(\\d+)(\\.(\\d+))?(\\*(\\d+))?");
|
|
10207
|
+
function optionalOBISMatchValue(value) {
|
|
10208
|
+
return value ?? "0";
|
|
10209
|
+
}
|
|
10185
10210
|
function OBIS2Hex(OBIS) {
|
|
10186
10211
|
const OBISElements = OBIS.match(OBIS_RegExpr);
|
|
10187
|
-
return OBISElements == null ? "000000000000" : parseInt(OBISElements[2]).toString(16).padStart(2, "0") + // optional A
|
|
10188
|
-
parseInt(OBISElements[4]).toString(16).padStart(2, "0") + // optional B
|
|
10189
|
-
parseInt(OBISElements[6]).toString(16).padStart(2, "0") + // mandatory C
|
|
10190
|
-
parseInt(OBISElements[7]).toString(16).padStart(2, "0") + // mandatory D
|
|
10191
|
-
parseInt(OBISElements[9]).toString(16).padStart(2, "0") + // optional E
|
|
10192
|
-
parseInt(OBISElements[11]).toString(16).padStart(2, "0");
|
|
10212
|
+
return OBISElements == null ? "000000000000" : parseInt(optionalOBISMatchValue(OBISElements[2])).toString(16).padStart(2, "0") + // optional A
|
|
10213
|
+
parseInt(optionalOBISMatchValue(OBISElements[4])).toString(16).padStart(2, "0") + // optional B
|
|
10214
|
+
parseInt(optionalOBISMatchValue(OBISElements[6])).toString(16).padStart(2, "0") + // mandatory C
|
|
10215
|
+
parseInt(optionalOBISMatchValue(OBISElements[7])).toString(16).padStart(2, "0") + // mandatory D
|
|
10216
|
+
parseInt(optionalOBISMatchValue(OBISElements[9])).toString(16).padStart(2, "0") + // optional E
|
|
10217
|
+
parseInt(optionalOBISMatchValue(OBISElements[11])).toString(16).padStart(2, "0");
|
|
10193
10218
|
}
|
|
10194
10219
|
function OBIS2MeasurementName(OBISString) {
|
|
10195
10220
|
switch (OBISString) {
|
|
@@ -10232,15 +10257,15 @@ function measurementName2human(In) {
|
|
|
10232
10257
|
}
|
|
10233
10258
|
}
|
|
10234
10259
|
function IsNullOrEmpty(value) {
|
|
10235
|
-
return
|
|
10260
|
+
return value == null || value.length === 0;
|
|
10236
10261
|
}
|
|
10237
10262
|
function WhenNullOrEmpty(value, replacement) {
|
|
10238
|
-
if (
|
|
10263
|
+
if (value == null || value.length === 0)
|
|
10239
10264
|
return replacement;
|
|
10240
10265
|
return value;
|
|
10241
10266
|
}
|
|
10242
10267
|
function hex2bin(hex, Reverse) {
|
|
10243
|
-
if (Reverse) {
|
|
10268
|
+
if (Reverse === true) {
|
|
10244
10269
|
const reversed = [];
|
|
10245
10270
|
for (let i = 0; i < hex.length; i += 2)
|
|
10246
10271
|
reversed.push(hex.substring(i, i + 2));
|
|
@@ -10354,7 +10379,7 @@ function SetHex(dv, hex, offset, reverse) {
|
|
|
10354
10379
|
const bytes = parseHexString(hex);
|
|
10355
10380
|
const buffer = new ArrayBuffer(bytes.length);
|
|
10356
10381
|
const tv = new DataView(buffer);
|
|
10357
|
-
if (reverse)
|
|
10382
|
+
if (reverse === true)
|
|
10358
10383
|
bytes.reverse();
|
|
10359
10384
|
for (let i = 0; i < bytes.length; i++) {
|
|
10360
10385
|
dv.setUint8(offset + i, bytes[i] ?? 0);
|
|
@@ -10403,7 +10428,7 @@ function SetUInt32(dv, value, offset, reverse) {
|
|
|
10403
10428
|
const bytes = getInt32Bytes(value);
|
|
10404
10429
|
const buffer = new ArrayBuffer(bytes.length);
|
|
10405
10430
|
const tv = new DataView(buffer);
|
|
10406
|
-
if (reverse)
|
|
10431
|
+
if (reverse === true)
|
|
10407
10432
|
bytes.reverse();
|
|
10408
10433
|
for (let i = 0; i < bytes.length; i++) {
|
|
10409
10434
|
dv.setUint8(offset + i, bytes[i] ?? 0);
|
|
@@ -10415,7 +10440,7 @@ function SetUInt64(dv, value, offset, reverse) {
|
|
|
10415
10440
|
const bytes = getInt64Bytes(value);
|
|
10416
10441
|
const buffer = new ArrayBuffer(bytes.length);
|
|
10417
10442
|
const tv = new DataView(buffer);
|
|
10418
|
-
if (reverse)
|
|
10443
|
+
if (reverse === true)
|
|
10419
10444
|
bytes.reverse();
|
|
10420
10445
|
for (let i = 0; i < bytes.length; i++) {
|
|
10421
10446
|
dv.setUint8(offset + i, bytes[i] ?? 0);
|
|
@@ -10447,7 +10472,7 @@ function SetUInt32_withCode(dv, value, scale, obis, offset, reverse) {
|
|
|
10447
10472
|
const obisBytes = getInt8Bytes(obis);
|
|
10448
10473
|
const buffer = new ArrayBuffer(valueBytes.length + 2);
|
|
10449
10474
|
const tv = new DataView(buffer);
|
|
10450
|
-
if (reverse)
|
|
10475
|
+
if (reverse === true)
|
|
10451
10476
|
valueBytes.reverse();
|
|
10452
10477
|
for (let i = 0; i < valueBytes.length; i++) {
|
|
10453
10478
|
dv.setUint8(offset + i, valueBytes[i] ?? 0);
|
|
@@ -10510,8 +10535,7 @@ function CreateDiv2(ParentDiv, ChildClassName, ChildAInnerHTML, ChildBInnerHTML)
|
|
|
10510
10535
|
return childDiv;
|
|
10511
10536
|
}
|
|
10512
10537
|
function pad(text, paddingValue) {
|
|
10513
|
-
|
|
10514
|
-
text = "";
|
|
10538
|
+
text ??= "";
|
|
10515
10539
|
return (text + Array(2 * paddingValue).join("0")).substring(0, 2 * paddingValue);
|
|
10516
10540
|
}
|
|
10517
10541
|
async function sha256(message) {
|
|
@@ -10657,7 +10681,7 @@ function jsonPrettyPrinter(value, KeyLookup) {
|
|
|
10657
10681
|
value,
|
|
10658
10682
|
0,
|
|
10659
10683
|
"",
|
|
10660
|
-
KeyLookup ?? ((
|
|
10684
|
+
KeyLookup ?? ((_path, key) => key)
|
|
10661
10685
|
);
|
|
10662
10686
|
}
|
|
10663
10687
|
function _jsonPrettyPrinter(value, indentLevel, path, keyLookup) {
|
|
@@ -10736,13 +10760,15 @@ var ACrypt = class {
|
|
|
10736
10760
|
}
|
|
10737
10761
|
AddToVisualBuffer(valueHEX, bufferDiv, lineDiv) {
|
|
10738
10762
|
const newText = CreateDiv(bufferDiv, "entry", valueHEX);
|
|
10763
|
+
const lineId = getArrayLikeElement(lineDiv.children, 0, "Missing visual buffer line id");
|
|
10764
|
+
const lineHex = getArrayLikeElement(lineDiv.children, 1, "Missing visual buffer line hex");
|
|
10739
10765
|
newText.onmouseenter = function() {
|
|
10740
|
-
|
|
10741
|
-
|
|
10766
|
+
lineId.classList.add("overEntry");
|
|
10767
|
+
lineHex.classList.add("overEntry");
|
|
10742
10768
|
};
|
|
10743
10769
|
newText.onmouseleave = function() {
|
|
10744
|
-
|
|
10745
|
-
|
|
10770
|
+
lineId.classList.remove("overEntry");
|
|
10771
|
+
lineHex.classList.remove("overEntry");
|
|
10746
10772
|
};
|
|
10747
10773
|
lineDiv.onmouseenter = function() {
|
|
10748
10774
|
newText.classList.add("overEntry");
|
|
@@ -10758,7 +10784,7 @@ var Alfen = class {
|
|
|
10758
10784
|
this.chargy = chargy;
|
|
10759
10785
|
}
|
|
10760
10786
|
bufferToHex(buffer, Reverse) {
|
|
10761
|
-
return (Reverse ? Array.from(new Uint8Array(buffer)).reverse() : Array.from(new Uint8Array(buffer))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
10787
|
+
return (Reverse === true ? Array.from(new Uint8Array(buffer)).reverse() : Array.from(new Uint8Array(buffer))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
10762
10788
|
}
|
|
10763
10789
|
bufferToNumber(buffer) {
|
|
10764
10790
|
return parseInt(Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join(""), 16);
|
|
@@ -10793,28 +10819,29 @@ var Alfen = class {
|
|
|
10793
10819
|
signedValues = Content;
|
|
10794
10820
|
let previousTimestamp = "";
|
|
10795
10821
|
for (let i = 0; i < signedValues.length; i++) {
|
|
10796
|
-
const elements = signedValues
|
|
10797
|
-
if (elements
|
|
10822
|
+
const elements = getArrayElement(signedValues, i, "Missing Alfen signed value").split(";");
|
|
10823
|
+
if (elements.length != 6 && elements.length != 7)
|
|
10798
10824
|
return {
|
|
10799
10825
|
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
10800
10826
|
message: this.chargy.GetMultilanguageText("Invalid number of signed meter values!"),
|
|
10801
10827
|
certainty: 0
|
|
10802
10828
|
};
|
|
10803
|
-
const FormatId = elements
|
|
10804
|
-
const Type = elements
|
|
10805
|
-
const BlobVersion = elements
|
|
10806
|
-
const
|
|
10807
|
-
const
|
|
10808
|
-
const
|
|
10829
|
+
const FormatId = getArrayElement(elements, 0, "Missing Alfen format id");
|
|
10830
|
+
const Type = getArrayElement(elements, 1, "Missing Alfen type");
|
|
10831
|
+
const BlobVersion = getArrayElement(elements, 2, "Missing Alfen blob version");
|
|
10832
|
+
const publicKeyValue = getArrayElement(elements, 3, "Missing Alfen public key");
|
|
10833
|
+
const PublicKey = this.chargy.base32Decode(publicKeyValue, "RFC4648");
|
|
10834
|
+
const DataSet = this.chargy.base32Decode(getArrayElement(elements, 4, "Missing Alfen data set"), "RFC4648");
|
|
10835
|
+
const Signature = this.chargy.base32Decode(getArrayElement(elements, 5, "Missing Alfen signature"), "RFC4648");
|
|
10809
10836
|
if (common.PublicKey === "")
|
|
10810
|
-
common.PublicKey =
|
|
10811
|
-
else if (
|
|
10837
|
+
common.PublicKey = publicKeyValue;
|
|
10838
|
+
else if (publicKeyValue !== common.PublicKey)
|
|
10812
10839
|
return {
|
|
10813
10840
|
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
10814
10841
|
message: this.chargy.GetMultilanguageText("Inconsistent public keys!"),
|
|
10815
10842
|
certainty: 0
|
|
10816
10843
|
};
|
|
10817
|
-
if (FormatId !== "AP" || Type
|
|
10844
|
+
if (FormatId !== "AP" || Type.length !== 1 || BlobVersion.length !== 1 || BlobVersion !== "3" || PublicKey.byteLength !== 25 || DataSet.byteLength !== 82 || Signature.byteLength !== 48) {
|
|
10818
10845
|
return {
|
|
10819
10846
|
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
10820
10847
|
message: this.chargy.GetMultilanguageText("Invalid charging session format!"),
|
|
@@ -10931,14 +10958,8 @@ var Alfen = class {
|
|
|
10931
10958
|
const evseId = asString(containerInfos["EVSEId"]) ?? "DE*GEF*EVSE*CHARGY*1";
|
|
10932
10959
|
const chargingStationId = asString(containerInfos["ChargingStationId"]) ?? "DE*GEF*STATION*CHARGY*1";
|
|
10933
10960
|
const chargingSessionId = asString(chargingSession?.["@id"]);
|
|
10934
|
-
const firstDataSet = common.dataSets
|
|
10935
|
-
const lastDataSet = common.dataSets
|
|
10936
|
-
if (firstDataSet === void 0 || lastDataSet === void 0)
|
|
10937
|
-
return {
|
|
10938
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
10939
|
-
message: this.chargy.GetMultilanguageText("Invalid number of signed meter values!"),
|
|
10940
|
-
certainty: 0
|
|
10941
|
-
};
|
|
10961
|
+
const firstDataSet = getFirstArrayElement(common.dataSets, "Missing first Alfen data set");
|
|
10962
|
+
const lastDataSet = getLastArrayElement(common.dataSets, "Missing last Alfen data set");
|
|
10942
10963
|
const _CTR = {
|
|
10943
10964
|
"@id": chargingSessionId ?? common.InternalSessionId.toString(),
|
|
10944
10965
|
"@context": "https://open.charging.cloud/contexts/CTR+json",
|
|
@@ -11219,62 +11240,62 @@ var AlfenCrypt01 = class extends ACrypt {
|
|
|
11219
11240
|
}
|
|
11220
11241
|
return {};
|
|
11221
11242
|
}
|
|
11222
|
-
async ViewMeasurement(measurementValue,
|
|
11243
|
+
async ViewMeasurement(measurementValue, _errorDiv, introDiv, infoDiv, PlainTextDiv, HashedPlainTextDiv, PublicKeyDiv, SignatureExpectedDiv, SignatureCheckDiv) {
|
|
11223
11244
|
const result = measurementValue.result;
|
|
11224
11245
|
{
|
|
11225
11246
|
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "AlfenCrypt01").replace("{cryptoAlgorithm}", this.description);
|
|
11226
11247
|
}
|
|
11227
11248
|
{
|
|
11228
|
-
if (PlainTextDiv.parentElement
|
|
11229
|
-
PlainTextDiv.parentElement.children
|
|
11249
|
+
if (PlainTextDiv.parentElement) {
|
|
11250
|
+
getArrayLikeElement(PlainTextDiv.parentElement.children, 0, "Missing plain text header").innerHTML = this.chargy.GetLocalizedMessage("Plain text") + " (320 Bytes, hex)";
|
|
11230
11251
|
}
|
|
11231
11252
|
PlainTextDiv.style.fontFamily = "";
|
|
11232
11253
|
PlainTextDiv.style.whiteSpace = "";
|
|
11233
11254
|
PlainTextDiv.style.maxHeight = "";
|
|
11234
11255
|
PlainTextDiv.style.overflowY = "";
|
|
11235
|
-
this.CreateLocalizedLine("Adapter Id", measurementValue.measurement.adapterId, result.adapterId
|
|
11236
|
-
this.CreateLocalizedLine("Adapter Firmware Version", measurementValue.measurement.adapterFWVersion, result.adapterFWVersion
|
|
11237
|
-
this.CreateLocalizedLine("Adapter Firmware Checksum", measurementValue.measurement.adapterFWChecksum, result.adapterFWChecksum
|
|
11238
|
-
this.CreateLocalizedLine("Meter number", measurementValue.measurement.energyMeterId, result.meterId
|
|
11256
|
+
this.CreateLocalizedLine("Adapter Id", measurementValue.measurement.adapterId, result.adapterId ?? "", infoDiv, PlainTextDiv);
|
|
11257
|
+
this.CreateLocalizedLine("Adapter Firmware Version", measurementValue.measurement.adapterFWVersion, result.adapterFWVersion ?? "", infoDiv, PlainTextDiv);
|
|
11258
|
+
this.CreateLocalizedLine("Adapter Firmware Checksum", measurementValue.measurement.adapterFWChecksum, result.adapterFWChecksum ?? "", infoDiv, PlainTextDiv);
|
|
11259
|
+
this.CreateLocalizedLine("Meter number", measurementValue.measurement.energyMeterId, result.meterId ?? "", infoDiv, PlainTextDiv);
|
|
11239
11260
|
this.CreateLocalizedLine(
|
|
11240
11261
|
"Meter status",
|
|
11241
11262
|
hex2bin(measurementValue.statusMeter, true) + " (" + measurementValue.statusMeter + ' hex)<br /><span class="statusInfos">' + this.DecodeMeterStatus(measurementValue.statusMeter).join("<br />") + "</span>",
|
|
11242
|
-
result.statusMeter
|
|
11263
|
+
result.statusMeter ?? "",
|
|
11243
11264
|
infoDiv,
|
|
11244
11265
|
PlainTextDiv
|
|
11245
11266
|
);
|
|
11246
11267
|
this.CreateLocalizedLine(
|
|
11247
11268
|
"Adapter status",
|
|
11248
11269
|
hex2bin(measurementValue.statusAdapter, true) + " (" + measurementValue.statusAdapter + ' hex)<br /><span class="statusInfos">' + this.DecodeAdapterStatus(measurementValue.statusAdapter).join("<br />") + "</span>",
|
|
11249
|
-
result.statusAdapter
|
|
11270
|
+
result.statusAdapter ?? "",
|
|
11250
11271
|
infoDiv,
|
|
11251
11272
|
PlainTextDiv
|
|
11252
11273
|
);
|
|
11253
|
-
this.CreateLocalizedLine("Seconds index", measurementValue.secondsIndex, result.secondsIndex
|
|
11254
|
-
this.CreateLocalizedLine("Timestamp", UTC2human(measurementValue.timestamp), result.timestamp
|
|
11255
|
-
this.CreateLocalizedLine("OBIS code", measurementValue.measurement.obis, result.obisId
|
|
11256
|
-
this.CreateLocalizedLine("Unit (encoded)", measurementValue.measurement.unitEncoded ?? 0, result.unitEncoded
|
|
11257
|
-
this.CreateLocalizedLine("Scaling", measurementValue.measurement.scale, result.scalar
|
|
11258
|
-
this.CreateLocalizedLine("Measurement Value", measurementValue.value.toString() + " Wh", result.value
|
|
11259
|
-
this.CreateLocalizedLine("Authorization", (measurementValue.measurement.chargingSession?.authorizationStart["@id"] ?? "") + " hex", pad(result.uid, 20)
|
|
11260
|
-
this.CreateLocalizedLine("SessionId", measurementValue.measurement.chargingSession?.["@id"] ?? "", result.sessionId
|
|
11261
|
-
this.CreateLocalizedLine("Pagination counter", measurementValue.paginationId, result.paging
|
|
11274
|
+
this.CreateLocalizedLine("Seconds index", measurementValue.secondsIndex, result.secondsIndex ?? "", infoDiv, PlainTextDiv);
|
|
11275
|
+
this.CreateLocalizedLine("Timestamp", UTC2human(measurementValue.timestamp), result.timestamp ?? "", infoDiv, PlainTextDiv);
|
|
11276
|
+
this.CreateLocalizedLine("OBIS code", measurementValue.measurement.obis, result.obisId ?? "", infoDiv, PlainTextDiv);
|
|
11277
|
+
this.CreateLocalizedLine("Unit (encoded)", measurementValue.measurement.unitEncoded ?? 0, result.unitEncoded ?? "", infoDiv, PlainTextDiv);
|
|
11278
|
+
this.CreateLocalizedLine("Scaling", measurementValue.measurement.scale, result.scalar ?? "", infoDiv, PlainTextDiv);
|
|
11279
|
+
this.CreateLocalizedLine("Measurement Value", measurementValue.value.toString() + " Wh", result.value ?? "", infoDiv, PlainTextDiv);
|
|
11280
|
+
this.CreateLocalizedLine("Authorization", (measurementValue.measurement.chargingSession?.authorizationStart["@id"] ?? "") + " hex", pad(result.uid, 20), infoDiv, PlainTextDiv);
|
|
11281
|
+
this.CreateLocalizedLine("SessionId", measurementValue.measurement.chargingSession?.["@id"] ?? "", result.sessionId ?? "", infoDiv, PlainTextDiv);
|
|
11282
|
+
this.CreateLocalizedLine("Pagination counter", measurementValue.paginationId, result.paging ?? "", infoDiv, PlainTextDiv);
|
|
11262
11283
|
}
|
|
11263
11284
|
{
|
|
11264
|
-
if (HashedPlainTextDiv.parentElement
|
|
11265
|
-
HashedPlainTextDiv.parentElement.children
|
|
11285
|
+
if (HashedPlainTextDiv.parentElement) {
|
|
11286
|
+
getArrayLikeElement(HashedPlainTextDiv.parentElement.children, 0, "Missing hashed plain text header").innerHTML = this.chargy.GetLocalizedMessage("Hashed plain text") + " (SHA256, hex)";
|
|
11266
11287
|
}
|
|
11267
11288
|
const hashValueText = typeof result.hashValue === "string" ? result.hashValue : result.hashValue?.toString(16) ?? "";
|
|
11268
11289
|
HashedPlainTextDiv.innerHTML = hashValueText.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
11269
11290
|
}
|
|
11270
|
-
if (result.publicKey && result.publicKey
|
|
11271
|
-
if (PublicKeyDiv.parentElement
|
|
11272
|
-
PublicKeyDiv.parentElement.children
|
|
11291
|
+
if (result.publicKey != null && result.publicKey.length > 0) {
|
|
11292
|
+
if (PublicKeyDiv.parentElement) {
|
|
11293
|
+
getArrayLikeElement(PublicKeyDiv.parentElement.children, 0, "Missing public key header").innerHTML = this.chargy.GetLocalizedMessage("Public Key") + " (" + (result.publicKeyFormat != null && result.publicKeyFormat.length > 0 ? result.publicKeyFormat + ", " : "") + "base32)";
|
|
11273
11294
|
}
|
|
11274
11295
|
if (!IsNullOrEmpty(result.publicKey))
|
|
11275
11296
|
PublicKeyDiv.innerHTML = result.publicKey.match(/.{1,4}/g)?.join(" ") ?? "";
|
|
11276
11297
|
}
|
|
11277
|
-
if (PublicKeyDiv.parentElement
|
|
11298
|
+
if (PublicKeyDiv.parentElement?.children[3]) {
|
|
11278
11299
|
PublicKeyDiv.parentElement.children[3].innerHTML = "";
|
|
11279
11300
|
if (result.publicKeySignatures) {
|
|
11280
11301
|
for (const signature of result.publicKeySignatures) {
|
|
@@ -11293,14 +11314,14 @@ var AlfenCrypt01 = class extends ACrypt {
|
|
|
11293
11314
|
}
|
|
11294
11315
|
}
|
|
11295
11316
|
if (result.signature != null) {
|
|
11296
|
-
if (SignatureExpectedDiv.parentElement
|
|
11297
|
-
SignatureExpectedDiv.parentElement.children
|
|
11317
|
+
if (SignatureExpectedDiv.parentElement) {
|
|
11318
|
+
getArrayLikeElement(SignatureExpectedDiv.parentElement.children, 0, "Missing expected signature header").innerHTML = this.chargy.GetLocalizedMessage("Expected signature") + " (" + (result.signature.format ?? "") + ", hex)";
|
|
11298
11319
|
}
|
|
11299
|
-
if (result.signature.r && result.signature.s) {
|
|
11320
|
+
if (result.signature.r != null && result.signature.r.length > 0 && result.signature.s != null && result.signature.s.length > 0) {
|
|
11300
11321
|
const signatureR = result.signature.r.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
11301
11322
|
const signatureS = result.signature.s.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
11302
11323
|
SignatureExpectedDiv.innerHTML = "r: " + signatureR + "<br />s: " + signatureS;
|
|
11303
|
-
} else if (result.signature.value)
|
|
11324
|
+
} else if (result.signature.value != null && result.signature.value.length > 0)
|
|
11304
11325
|
SignatureExpectedDiv.innerHTML = result.signature.value.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
11305
11326
|
}
|
|
11306
11327
|
{
|
|
@@ -11542,10 +11563,14 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11542
11563
|
throw new Error(`Invalid identification of signed meter value #${String(measurementCounter - 1)}!`);
|
|
11543
11564
|
const previousParts = previousId.split("-");
|
|
11544
11565
|
const currentParts = currentId.split("-");
|
|
11545
|
-
|
|
11566
|
+
const previousPrefix = previousParts.at(0);
|
|
11567
|
+
const previousCounter = previousParts.at(1);
|
|
11568
|
+
const currentPrefix = currentParts.at(0);
|
|
11569
|
+
const currentCounter = currentParts.at(1);
|
|
11570
|
+
if (previousParts.length !== 2 || currentParts.length !== 2 || previousPrefix !== currentPrefix || previousCounter === void 0 || currentCounter === void 0 || // parseInt returns NaN in case parsing fails which in turn does not match
|
|
11546
11571
|
// anything (not even NaN). This way we are checking that both counter values
|
|
11547
11572
|
// are numeric too.
|
|
11548
|
-
parseInt(
|
|
11573
|
+
parseInt(previousCounter, 10) >= parseInt(currentCounter, 10)) {
|
|
11549
11574
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeasurementIdP", measurementCounter)));
|
|
11550
11575
|
}
|
|
11551
11576
|
}
|
|
@@ -11583,7 +11608,7 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11583
11608
|
precision: asNumber(displayedFormat?.["precision"])
|
|
11584
11609
|
};
|
|
11585
11610
|
});
|
|
11586
|
-
if (currentMeasurement["meterInfo"]) {
|
|
11611
|
+
if (currentMeasurement["meterInfo"] != null) {
|
|
11587
11612
|
const meterInfoObj2 = asJSONObject(currentMeasurement["meterInfo"]);
|
|
11588
11613
|
if (meterInfoObj2?.["firmwareVersion"] !== common.meterInfo_firmwareVersion)
|
|
11589
11614
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeterInfo_FirmwareVersionP", measurementCounter)));
|
|
@@ -11591,42 +11616,42 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11591
11616
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeterInfo_PublicKeyP", measurementCounter)));
|
|
11592
11617
|
if (meterInfoObj2?.["meterId"] !== common.meterInfo_meterId)
|
|
11593
11618
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeterInfo_MeterIdP", measurementCounter)));
|
|
11594
|
-
if (meterInfoObj2?.["meterId"] !== additionalValues.
|
|
11619
|
+
if (meterInfoObj2?.["meterId"] !== additionalValues.find((element) => element.measurandName === "MA1")?.value)
|
|
11595
11620
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeterInfo_MeterIdP", measurementCounter)));
|
|
11596
11621
|
if (meterInfoObj2?.["manufacturer"] !== common.meterInfo_manufacturer)
|
|
11597
11622
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeterInfo_ManufacturerP", measurementCounter)));
|
|
11598
11623
|
if (meterInfoObj2?.["type"] !== common.meterInfo_type)
|
|
11599
11624
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_MeterInfo_TypeP", measurementCounter)));
|
|
11600
11625
|
}
|
|
11601
|
-
if (currentMeasurement["contract"]) {
|
|
11626
|
+
if (currentMeasurement["contract"] != null) {
|
|
11602
11627
|
const contractObj2 = asJSONObject(currentMeasurement["contract"]);
|
|
11603
|
-
if (additionalValues.filter((element) => element.measurandName?.startsWith("Meta") && asString(element.value)?.startsWith("contract-id:")).length == 0)
|
|
11628
|
+
if (additionalValues.filter((element) => element.measurandName?.startsWith("Meta") === true && asString(element.value)?.startsWith("contract-id:") === true).length == 0)
|
|
11604
11629
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_Contract_IdP", measurementCounter)));
|
|
11605
11630
|
if (contractObj2?.["id"] !== common.contract_id)
|
|
11606
11631
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_Contract_IdP", measurementCounter)));
|
|
11607
11632
|
if (contractObj2?.["type"] !== common.contract_type)
|
|
11608
11633
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_Contract_TypeP", measurementCounter)));
|
|
11609
|
-
const contractInfo = additionalValues.
|
|
11634
|
+
const contractInfo = additionalValues.find((element) => element.measurandName?.startsWith("Meta") === true && asString(element.value)?.startsWith("contract-id:") === true)?.value;
|
|
11610
11635
|
if (contractInfo != null) {
|
|
11611
|
-
if (contractObj2?.["type"] && contractInfo !== "contract-id: " + (common.contract_type ?? "-") + ":" + common.contract_id)
|
|
11636
|
+
if (contractObj2?.["type"] != null && contractInfo !== "contract-id: " + (common.contract_type ?? "-") + ":" + common.contract_id)
|
|
11612
11637
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_Contract_IdP", measurementCounter)));
|
|
11613
|
-
if (
|
|
11638
|
+
if (contractObj2?.["type"] == null && contractInfo !== "contract-id: " + common.contract_id)
|
|
11614
11639
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_Contract_IdP", measurementCounter)));
|
|
11615
11640
|
}
|
|
11616
11641
|
}
|
|
11617
11642
|
const rcrInAdditional = additionalValues.find((element) => element.measurandName == "RCR");
|
|
11618
11643
|
if (currentMeasurement["value"] == null)
|
|
11619
11644
|
throw new Error(`Missing value within signed meter value #${String(measurementCounter)}!`);
|
|
11620
|
-
if ((valueObj2?.["measurand"] || valueObj2?.["measuredValue"]) && rcrInAdditional === void 0)
|
|
11645
|
+
if ((valueObj2?.["measurand"] != null || valueObj2?.["measuredValue"] != null) && rcrInAdditional === void 0)
|
|
11621
11646
|
throw new Error(`Missing 'RCR' within the additional values of signed meter value #${String(measurementCounter)}!`);
|
|
11622
|
-
if (valueObj2?.["measurand"]) {
|
|
11647
|
+
if (valueObj2?.["measurand"] != null) {
|
|
11623
11648
|
const measurandObj2 = asJSONObject(valueObj2["measurand"]);
|
|
11624
11649
|
if (measurandObj2?.["id"] !== common.value_measurand_id || measurandObj2["id"] !== rcrInAdditional?.measurandId)
|
|
11625
11650
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_Measurand_IdentificationP", measurementCounter)));
|
|
11626
11651
|
if (measurandObj2?.["name"] !== common.value_measurand_name || measurandObj2["name"] !== rcrInAdditional?.measurandName)
|
|
11627
11652
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_Measurand_NameP", measurementCounter)));
|
|
11628
11653
|
}
|
|
11629
|
-
if (valueObj2?.["measuredValue"]) {
|
|
11654
|
+
if (valueObj2?.["measuredValue"] != null) {
|
|
11630
11655
|
if (measuredValueObj2?.["value"] !== rcrInAdditional?.value)
|
|
11631
11656
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValueP", measurementCounter)));
|
|
11632
11657
|
if (measuredValueObj2?.["scale"] !== common.value_measuredValue_scale || measuredValueObj2?.["scale"] !== rcrInAdditional?.scale)
|
|
@@ -11638,15 +11663,15 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11638
11663
|
if (measuredValueObj2?.["valueType"] !== common.value_measuredValue_valueType || measuredValueObj2?.["valueType"] !== rcrInAdditional?.valueType)
|
|
11639
11664
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_SignedMeterValue_TypeP", measurementCounter)));
|
|
11640
11665
|
}
|
|
11641
|
-
if (currentMeasurement["chargePoint"] && asJSONObject(currentMeasurement["chargePoint"])?.["softwareVersion"] !== common.chargePoint_softwareVersion)
|
|
11666
|
+
if (currentMeasurement["chargePoint"] != null && asJSONObject(currentMeasurement["chargePoint"])?.["softwareVersion"] !== common.chargePoint_softwareVersion)
|
|
11642
11667
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageText("Inconsistent_ChargingStation_FirmwareVersion")));
|
|
11643
|
-
const signedEVSEId = additionalValues.filter((element) => element.measurandName?.startsWith("Meta") && asString(element.value)?.startsWith("evse-id:"));
|
|
11668
|
+
const signedEVSEId = additionalValues.filter((element) => element.measurandName?.startsWith("Meta") === true && asString(element.value)?.startsWith("evse-id:") === true);
|
|
11644
11669
|
if (signedEVSEId.length == 1) {
|
|
11645
11670
|
const evse__id = (asString(signedEVSEId[0]?.value) ?? "").replace("evse-id:", "").trim();
|
|
11646
11671
|
if (evse__id !== "unknown" && ExpectedEVSEId !== evse__id)
|
|
11647
11672
|
currentErrors.push(CreateError(this.chargy.GetMultilanguageText("Inconsistent_EVSE_Identification")));
|
|
11648
11673
|
}
|
|
11649
|
-
const signedCSCSWVersion = additionalValues.filter((element) => element.measurandName?.startsWith("Meta") && asString(element.value)?.startsWith("csc-sw-version:"));
|
|
11674
|
+
const signedCSCSWVersion = additionalValues.filter((element) => element.measurandName?.startsWith("Meta") === true && asString(element.value)?.startsWith("csc-sw-version:") === true);
|
|
11650
11675
|
if (signedCSCSWVersion.length == 1) {
|
|
11651
11676
|
const csc_sw_version = asString(signedCSCSWVersion[0]?.value)?.replace("csc-sw-version:", "").trim() ?? null;
|
|
11652
11677
|
if (previousCscSwVersion !== null && previousCscSwVersion !== csc_sw_version)
|
|
@@ -11711,6 +11736,8 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11711
11736
|
case "Evt":
|
|
11712
11737
|
Evt = asNumber(additionalValue.value) ?? NaN;
|
|
11713
11738
|
break;
|
|
11739
|
+
default:
|
|
11740
|
+
break;
|
|
11714
11741
|
}
|
|
11715
11742
|
}
|
|
11716
11743
|
const currentRCRValue = asNumber(RCR?.value) ?? NaN;
|
|
@@ -11773,18 +11800,17 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11773
11800
|
});
|
|
11774
11801
|
}
|
|
11775
11802
|
const n = common.dataSets.length - 1;
|
|
11776
|
-
const firstDataSet = common.dataSets
|
|
11777
|
-
const lastDataSet = common.dataSets
|
|
11778
|
-
if (firstDataSet
|
|
11803
|
+
const firstDataSet = getFirstArrayElement(common.dataSets, "Missing first BSM data set");
|
|
11804
|
+
const lastDataSet = getArrayElement(common.dataSets, n, "Missing last BSM data set");
|
|
11805
|
+
if (firstDataSet.TypParsed !== "START" && firstDataSet.TypParsed !== "TURN ON")
|
|
11779
11806
|
throw new Error(this.chargy.GetLocalizedMessageWithParameter("Inconsistent_EnergyMeterValueP", 1));
|
|
11780
11807
|
for (let i = 1; i < common.dataSets.length - 1; i++) {
|
|
11781
11808
|
if (common.dataSets[i]?.TypParsed !== "CURRENT")
|
|
11782
11809
|
errors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("Inconsistent_EnergyMeterValueP", measurementCounter + 1)));
|
|
11783
11810
|
}
|
|
11784
|
-
if (lastDataSet
|
|
11811
|
+
if (lastDataSet.TypParsed !== "END" && lastDataSet.TypParsed !== "TURN OFF")
|
|
11785
11812
|
throw new Error(this.chargy.GetLocalizedMessageWithParameter("Inconsistent_EnergyMeterValueP", n + 1));
|
|
11786
|
-
|
|
11787
|
-
CTR.chargingSessions = [];
|
|
11813
|
+
CTR.chargingSessions ??= [];
|
|
11788
11814
|
const ASN1_SignatureSchema = this.chargy.asn1.define("Signature", function() {
|
|
11789
11815
|
this.seq().obj(
|
|
11790
11816
|
this.key("r").int(),
|
|
@@ -11901,12 +11927,12 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
11901
11927
|
CTR.chargingSessions.push(session);
|
|
11902
11928
|
CTR["@id"] = common.meterInfo_meterId + "-" + String(firstDataSet.Epoch);
|
|
11903
11929
|
{
|
|
11904
|
-
const firstChargingSession = CTR.chargingSessions
|
|
11905
|
-
const lastChargingSession = CTR.chargingSessions
|
|
11906
|
-
if (
|
|
11930
|
+
const firstChargingSession = getFirstArrayElement(CTR.chargingSessions, "Missing first BSM charging session");
|
|
11931
|
+
const lastChargingSession = getLastArrayElement(CTR.chargingSessions, "Missing last BSM charging session");
|
|
11932
|
+
if (CTR.begin == void 0 || CTR.begin === "" || CTR.begin > firstChargingSession.begin) {
|
|
11907
11933
|
CTR.begin = firstChargingSession.begin;
|
|
11908
11934
|
}
|
|
11909
|
-
if (lastChargingSession
|
|
11935
|
+
if (lastChargingSession.end !== void 0) {
|
|
11910
11936
|
if (CTR.end == void 0 || CTR.end === "" || CTR.end < lastChargingSession.end)
|
|
11911
11937
|
CTR.end = lastChargingSession.end;
|
|
11912
11938
|
}
|
|
@@ -12078,58 +12104,63 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
12078
12104
|
}
|
|
12079
12105
|
return {};
|
|
12080
12106
|
}
|
|
12081
|
-
async ViewMeasurement(measurementValue,
|
|
12107
|
+
async ViewMeasurement(measurementValue, _errorDiv, introDiv, infoDiv, PlainTextDiv, HashedPlainTextDiv, PublicKeyDiv, SignatureExpectedDiv, SignatureCheckDiv) {
|
|
12082
12108
|
if (measurementValue.measurement === void 0)
|
|
12083
12109
|
return new Error("Invalid measurement!");
|
|
12084
12110
|
const result = measurementValue.result;
|
|
12085
12111
|
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "BSMCrypt01").replace("{cryptoAlgorithm}", this.description);
|
|
12086
|
-
if (PlainTextDiv.parentElement
|
|
12087
|
-
PlainTextDiv.parentElement.children
|
|
12112
|
+
if (PlainTextDiv.parentElement) {
|
|
12113
|
+
getArrayLikeElement(PlainTextDiv.parentElement.children, 0, "Missing plain text header").innerHTML = "Plain text (" + measurementValue.result.ArraySize.toString() + " Bytes, hex)";
|
|
12088
12114
|
}
|
|
12089
12115
|
PlainTextDiv.style.fontFamily = "";
|
|
12090
12116
|
PlainTextDiv.style.whiteSpace = "";
|
|
12091
12117
|
PlainTextDiv.style.maxHeight = "";
|
|
12092
12118
|
PlainTextDiv.style.overflowY = "";
|
|
12093
|
-
this.CreateLine("Snapshot-Typ", this.ParseTyp(measurementValue.Typ), result.Typ
|
|
12094
|
-
this.CreateLine("RCR", (measurementValue.RCR * 10 ** measurementValue.RCR_SF).toString() + " " + measurementValue.RCR_Unit, result.RCR
|
|
12095
|
-
this.CreateLine("TotWhImp", (measurementValue.TotWhImp * 10 ** measurementValue.TotWhImp_SF).toString() + " " + measurementValue.TotWhImp_Unit, result.TotWhImp
|
|
12096
|
-
this.CreateLine("W", (measurementValue.W * 10 ** measurementValue.W_SF).toString() + " " + measurementValue.W_Unit, result.W
|
|
12097
|
-
this.CreateLine("MA1", measurementValue.MA1, result.MA1
|
|
12098
|
-
this.CreateLine("RCnt", measurementValue.RCnt, result.RCnt
|
|
12099
|
-
this.CreateLine("OS", measurementValue.OS, result.OS
|
|
12100
|
-
this.CreateLine("Zeitstempel", UTC2human(measurementValue.Epoch), result.Epoch
|
|
12101
|
-
this.CreateLine("TZO", measurementValue.TZO.toString() + " Minuten", result.TZO
|
|
12102
|
-
this.CreateLine("EpochSetCnt", measurementValue.EpochSetCnt, result.EpochSetCnt
|
|
12103
|
-
this.CreateLine("EpochSetOS", measurementValue.EpochSetOS, result.EpochSetOS
|
|
12104
|
-
this.CreateLine("DI", measurementValue.DI, result.DI
|
|
12105
|
-
this.CreateLine("DO", measurementValue.DO, result.DO
|
|
12106
|
-
this.CreateLine("Meta1", measurementValue.Meta1, result.Meta1
|
|
12107
|
-
this.CreateLine("Meta2", measurementValue.Meta2, result.Meta2
|
|
12108
|
-
this.CreateLine("Meta3", measurementValue.Meta3, result.Meta3
|
|
12109
|
-
this.CreateLine("Evt", this.ParseEvents(measurementValue.Evt).join("<br>"), result.Evt
|
|
12110
|
-
if (HashedPlainTextDiv.parentElement
|
|
12111
|
-
HashedPlainTextDiv.parentElement.children
|
|
12119
|
+
this.CreateLine("Snapshot-Typ", this.ParseTyp(measurementValue.Typ), result.Typ, infoDiv, PlainTextDiv);
|
|
12120
|
+
this.CreateLine("RCR", (measurementValue.RCR * 10 ** measurementValue.RCR_SF).toString() + " " + measurementValue.RCR_Unit, result.RCR, infoDiv, PlainTextDiv);
|
|
12121
|
+
this.CreateLine("TotWhImp", (measurementValue.TotWhImp * 10 ** measurementValue.TotWhImp_SF).toString() + " " + measurementValue.TotWhImp_Unit, result.TotWhImp, infoDiv, PlainTextDiv);
|
|
12122
|
+
this.CreateLine("W", (measurementValue.W * 10 ** measurementValue.W_SF).toString() + " " + measurementValue.W_Unit, result.W, infoDiv, PlainTextDiv);
|
|
12123
|
+
this.CreateLine("MA1", measurementValue.MA1, result.MA1, infoDiv, PlainTextDiv);
|
|
12124
|
+
this.CreateLine("RCnt", measurementValue.RCnt, result.RCnt, infoDiv, PlainTextDiv);
|
|
12125
|
+
this.CreateLine("OS", measurementValue.OS, result.OS, infoDiv, PlainTextDiv);
|
|
12126
|
+
this.CreateLine("Zeitstempel", UTC2human(measurementValue.Epoch), result.Epoch, infoDiv, PlainTextDiv);
|
|
12127
|
+
this.CreateLine("TZO", measurementValue.TZO.toString() + " Minuten", result.TZO, infoDiv, PlainTextDiv);
|
|
12128
|
+
this.CreateLine("EpochSetCnt", measurementValue.EpochSetCnt, result.EpochSetCnt, infoDiv, PlainTextDiv);
|
|
12129
|
+
this.CreateLine("EpochSetOS", measurementValue.EpochSetOS, result.EpochSetOS, infoDiv, PlainTextDiv);
|
|
12130
|
+
this.CreateLine("DI", measurementValue.DI, result.DI, infoDiv, PlainTextDiv);
|
|
12131
|
+
this.CreateLine("DO", measurementValue.DO, result.DO, infoDiv, PlainTextDiv);
|
|
12132
|
+
this.CreateLine("Meta1", measurementValue.Meta1, result.Meta1, infoDiv, PlainTextDiv);
|
|
12133
|
+
this.CreateLine("Meta2", measurementValue.Meta2, result.Meta2, infoDiv, PlainTextDiv);
|
|
12134
|
+
this.CreateLine("Meta3", measurementValue.Meta3, result.Meta3, infoDiv, PlainTextDiv);
|
|
12135
|
+
this.CreateLine("Evt", this.ParseEvents(measurementValue.Evt).join("<br>"), result.Evt, infoDiv, PlainTextDiv);
|
|
12136
|
+
if (HashedPlainTextDiv.parentElement) {
|
|
12137
|
+
getArrayLikeElement(HashedPlainTextDiv.parentElement.children, 0, "Missing hashed plain text header").innerHTML = "Hashed plain text (SHA256, hex)";
|
|
12112
12138
|
}
|
|
12113
12139
|
HashedPlainTextDiv.innerHTML = result.sha256value?.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
12114
12140
|
if (result.publicKey != null && result.publicKey != "") {
|
|
12115
|
-
if (PublicKeyDiv.parentElement
|
|
12116
|
-
PublicKeyDiv.parentElement.children
|
|
12141
|
+
if (PublicKeyDiv.parentElement) {
|
|
12142
|
+
getArrayLikeElement(PublicKeyDiv.parentElement.children, 0, "Missing public key header").innerHTML = "Public Key (" + (result.publicKeyFormat != null && result.publicKeyFormat.length > 0 ? result.publicKeyFormat + ", " : "") + "hex)";
|
|
12117
12143
|
}
|
|
12118
12144
|
if (!IsNullOrEmpty(result.publicKey))
|
|
12119
12145
|
PublicKeyDiv.innerHTML = result.publicKey.startsWith("04") ? '<span class="leadingFour">04</span> ' + (result.publicKey.substring(2).match(/.{1,8}/g)?.join(" ") ?? "-") : result.publicKey.match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
12120
|
-
|
|
12121
|
-
|
|
12146
|
+
const publicKeySignatureContainer = PublicKeyDiv.parentElement?.children[3];
|
|
12147
|
+
if (publicKeySignatureContainer !== void 0) {
|
|
12148
|
+
publicKeySignatureContainer.innerHTML = "";
|
|
12122
12149
|
}
|
|
12123
12150
|
if (result.publicKeySignatures) {
|
|
12124
12151
|
for (const signature of result.publicKeySignatures) {
|
|
12125
12152
|
try {
|
|
12126
|
-
const signatureDiv =
|
|
12127
|
-
|
|
12153
|
+
const signatureDiv = publicKeySignatureContainer?.appendChild(document.createElement("div"));
|
|
12154
|
+
const chargingSession = measurementValue.measurement.chargingSession;
|
|
12155
|
+
const evse = chargingSession?.EVSE;
|
|
12156
|
+
const meter = evse?.meters.at(0);
|
|
12157
|
+
const publicKey = meter?.publicKeys?.at(0);
|
|
12158
|
+
if (signatureDiv != null && chargingSession != null && evse != null && meter != null && publicKey != null) {
|
|
12128
12159
|
signatureDiv.innerHTML = await this.chargy.CheckMeterPublicKeySignature(
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
|
|
12160
|
+
chargingSession.chargingStation,
|
|
12161
|
+
evse,
|
|
12162
|
+
meter,
|
|
12163
|
+
publicKey,
|
|
12133
12164
|
signature
|
|
12134
12165
|
);
|
|
12135
12166
|
}
|
|
@@ -12140,12 +12171,12 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
12140
12171
|
}
|
|
12141
12172
|
}
|
|
12142
12173
|
if (result.signature != null) {
|
|
12143
|
-
if (SignatureExpectedDiv.parentElement
|
|
12144
|
-
SignatureExpectedDiv.parentElement.children
|
|
12174
|
+
if (SignatureExpectedDiv.parentElement) {
|
|
12175
|
+
getArrayLikeElement(SignatureExpectedDiv.parentElement.children, 0, "Missing expected signature header").innerHTML = "Erwartete Signatur (" + (result.signature.format ?? "") + ", hex)";
|
|
12145
12176
|
}
|
|
12146
|
-
if (result.signature.r && result.signature.s)
|
|
12177
|
+
if (result.signature.r != null && result.signature.r.length > 0 && result.signature.s != null && result.signature.s.length > 0)
|
|
12147
12178
|
SignatureExpectedDiv.innerHTML = "r: " + (result.signature.r.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-") + "<br />s: " + (result.signature.s.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-");
|
|
12148
|
-
else if (result.signature.value)
|
|
12179
|
+
else if (result.signature.value != null && result.signature.value.length > 0)
|
|
12149
12180
|
SignatureExpectedDiv.innerHTML = result.signature.value.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
12150
12181
|
}
|
|
12151
12182
|
switch (result.status) {
|
|
@@ -12171,6 +12202,7 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
12171
12202
|
SignatureCheckDiv.innerHTML = '<i class="fas fa-times-circle"></i><div id="description">Ung\xFCltige Signatur</div>';
|
|
12172
12203
|
break;
|
|
12173
12204
|
}
|
|
12205
|
+
return void 0;
|
|
12174
12206
|
}
|
|
12175
12207
|
//#region Helper methods
|
|
12176
12208
|
PrefixConverter(input) {
|
|
@@ -12249,30 +12281,35 @@ var BSMCrypt01 = class extends ACrypt {
|
|
|
12249
12281
|
if ((value & 1 << 29) != 0) events.push("OEM 15");
|
|
12250
12282
|
return events;
|
|
12251
12283
|
}
|
|
12252
|
-
DecodeStatus(statusValue)
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12284
|
+
// private DecodeStatus(statusValue: string) : Array<string>
|
|
12285
|
+
// {
|
|
12286
|
+
// const statusArray:string[] = [];
|
|
12287
|
+
// try
|
|
12288
|
+
// {
|
|
12289
|
+
// const status = parseInt(statusValue);
|
|
12290
|
+
// if ((status & 1) == 1)
|
|
12291
|
+
// statusArray.push("Fehler erkannt");
|
|
12292
|
+
// if ((status & 2) == 2)
|
|
12293
|
+
// statusArray.push("Synchrone Messwertübermittlung");
|
|
12294
|
+
// // Bit 3 is reserved!
|
|
12295
|
+
// if ((status & 8) == 8)
|
|
12296
|
+
// statusArray.push("System-Uhr ist synchron");
|
|
12297
|
+
// else
|
|
12298
|
+
// statusArray.push("System-Uhr ist nicht synchron");
|
|
12299
|
+
// if ((status & 16) == 16)
|
|
12300
|
+
// statusArray.push("Rücklaufsperre aktiv");
|
|
12301
|
+
// if ((status & 32) == 32)
|
|
12302
|
+
// statusArray.push("Energierichtung -A");
|
|
12303
|
+
// if ((status & 64) == 64)
|
|
12304
|
+
// statusArray.push("Magnetfeld erkannt");
|
|
12305
|
+
// }
|
|
12306
|
+
// catch
|
|
12307
|
+
// {
|
|
12308
|
+
// statusArray.push("Invalid status!");
|
|
12309
|
+
// }
|
|
12310
|
+
// return statusArray;
|
|
12311
|
+
// }
|
|
12312
|
+
// //#endregion
|
|
12276
12313
|
};
|
|
12277
12314
|
|
|
12278
12315
|
// src/CanonicalJSON.ts
|
|
@@ -12672,7 +12709,7 @@ var EMHCrypt01 = class extends ACrypt {
|
|
|
12672
12709
|
measurementValue.result = cryptoResult;
|
|
12673
12710
|
return cryptoResult;
|
|
12674
12711
|
}
|
|
12675
|
-
if (measurementValue.measurement
|
|
12712
|
+
if (measurementValue.measurement?.chargingSession === void 0) {
|
|
12676
12713
|
return {
|
|
12677
12714
|
status: "InvalidMeasurement" /* InvalidMeasurement */
|
|
12678
12715
|
};
|
|
@@ -12711,9 +12748,10 @@ var EMHCrypt01 = class extends ACrypt {
|
|
|
12711
12748
|
cryptoResult.meter = meter;
|
|
12712
12749
|
if (meter.publicKeys != null && meter.publicKeys.length > 0) {
|
|
12713
12750
|
try {
|
|
12714
|
-
|
|
12715
|
-
cryptoResult.
|
|
12716
|
-
cryptoResult.
|
|
12751
|
+
const publicKey = meter.publicKeys.at(0);
|
|
12752
|
+
cryptoResult.publicKey = publicKey?.value?.toLowerCase();
|
|
12753
|
+
cryptoResult.publicKeyFormat = publicKey?.format;
|
|
12754
|
+
cryptoResult.publicKeySignatures = publicKey?.signatures;
|
|
12717
12755
|
try {
|
|
12718
12756
|
if (this.curve.keyFromPublic(cryptoResult.publicKey ?? "", "hex").verify(
|
|
12719
12757
|
cryptoResult.sha256value,
|
|
@@ -12738,59 +12776,60 @@ var EMHCrypt01 = class extends ACrypt {
|
|
|
12738
12776
|
}
|
|
12739
12777
|
return {};
|
|
12740
12778
|
}
|
|
12741
|
-
async ViewMeasurement(measurementValue,
|
|
12742
|
-
if (measurementValue.measurement
|
|
12779
|
+
async ViewMeasurement(measurementValue, _errorDiv, introDiv, infoDiv, PlainTextDiv, HashedPlainTextDiv, PublicKeyDiv, SignatureExpectedDiv, SignatureCheckDiv) {
|
|
12780
|
+
if (measurementValue.measurement?.chargingSession?.authorizationStart.timestamp === void 0) {
|
|
12743
12781
|
return new Error("Invalid measurement!");
|
|
12744
12782
|
}
|
|
12745
12783
|
const result = measurementValue.result;
|
|
12746
12784
|
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "EMHCrypt01").replace("{cryptoAlgorithm}", this.description);
|
|
12747
12785
|
{
|
|
12748
|
-
if (PlainTextDiv.parentElement
|
|
12749
|
-
PlainTextDiv.parentElement.children
|
|
12786
|
+
if (PlainTextDiv.parentElement) {
|
|
12787
|
+
getArrayLikeElement(PlainTextDiv.parentElement.children, 0, "Missing plain text header").innerHTML = "Plain text (320 Bytes, hex)";
|
|
12750
12788
|
}
|
|
12751
12789
|
PlainTextDiv.style.fontFamily = "";
|
|
12752
12790
|
PlainTextDiv.style.whiteSpace = "";
|
|
12753
12791
|
PlainTextDiv.style.maxHeight = "";
|
|
12754
12792
|
PlainTextDiv.style.overflowY = "";
|
|
12755
|
-
this.CreateLine("Z\xE4hlernummer", measurementValue.measurement.energyMeterId, result.meterId
|
|
12756
|
-
this.CreateLine("Zeitstempel", UTC2human(measurementValue.timestamp), result.timestamp
|
|
12757
|
-
this.CreateLine("Status", hex2bin(measurementValue.infoStatus) + " (" + measurementValue.infoStatus + ' hex)<br /><span class="statusInfos">' + this.DecodeStatus(measurementValue.infoStatus).join("<br />") + "</span>", result.infoStatus
|
|
12758
|
-
this.CreateLine("Sekundenindex", measurementValue.secondsIndex, result.secondsIndex
|
|
12759
|
-
this.CreateLine("Paginierungsz\xE4hler", parseInt(measurementValue.paginationId, 16), result.paginationId
|
|
12760
|
-
this.CreateLine("OBIS-Kennzahl", measurementValue.measurement.obis, result.obis
|
|
12761
|
-
this.CreateLine("Einheit (codiert)", measurementValue.measurement.unitEncoded ?? 0, result.unitEncoded
|
|
12762
|
-
this.CreateLine("Skalierung", measurementValue.measurement.scale, result.scale
|
|
12763
|
-
this.CreateLine("Messwert", measurementValue.value.toString() + " Wh", result.value
|
|
12764
|
-
this.CreateLine("Logbuchindex", measurementValue.logBookIndex + " hex", result.logBookIndex
|
|
12765
|
-
this.CreateLine("Autorisierung", measurementValue.measurement.chargingSession.authorizationStart["@id"] + " hex", pad(result.authorizationStart, 128)
|
|
12766
|
-
this.CreateLine("Autorisierungszeitpunkt", UTC2human(measurementValue.measurement.chargingSession.authorizationStart.timestamp), pad(result.authorizationStartTimestamp, 151)
|
|
12793
|
+
this.CreateLine("Z\xE4hlernummer", measurementValue.measurement.energyMeterId, result.meterId ?? "", infoDiv, PlainTextDiv);
|
|
12794
|
+
this.CreateLine("Zeitstempel", UTC2human(measurementValue.timestamp), result.timestamp ?? "", infoDiv, PlainTextDiv);
|
|
12795
|
+
this.CreateLine("Status", hex2bin(measurementValue.infoStatus) + " (" + measurementValue.infoStatus + ' hex)<br /><span class="statusInfos">' + this.DecodeStatus(measurementValue.infoStatus).join("<br />") + "</span>", result.infoStatus ?? "", infoDiv, PlainTextDiv);
|
|
12796
|
+
this.CreateLine("Sekundenindex", measurementValue.secondsIndex, result.secondsIndex ?? "", infoDiv, PlainTextDiv);
|
|
12797
|
+
this.CreateLine("Paginierungsz\xE4hler", parseInt(measurementValue.paginationId, 16), result.paginationId ?? "", infoDiv, PlainTextDiv);
|
|
12798
|
+
this.CreateLine("OBIS-Kennzahl", measurementValue.measurement.obis, result.obis ?? "", infoDiv, PlainTextDiv);
|
|
12799
|
+
this.CreateLine("Einheit (codiert)", measurementValue.measurement.unitEncoded ?? 0, result.unitEncoded ?? "", infoDiv, PlainTextDiv);
|
|
12800
|
+
this.CreateLine("Skalierung", measurementValue.measurement.scale, result.scale ?? "", infoDiv, PlainTextDiv);
|
|
12801
|
+
this.CreateLine("Messwert", measurementValue.value.toString() + " Wh", result.value ?? "", infoDiv, PlainTextDiv);
|
|
12802
|
+
this.CreateLine("Logbuchindex", measurementValue.logBookIndex + " hex", result.logBookIndex ?? "", infoDiv, PlainTextDiv);
|
|
12803
|
+
this.CreateLine("Autorisierung", measurementValue.measurement.chargingSession.authorizationStart["@id"] + " hex", pad(result.authorizationStart, 128), infoDiv, PlainTextDiv);
|
|
12804
|
+
this.CreateLine("Autorisierungszeitpunkt", UTC2human(measurementValue.measurement.chargingSession.authorizationStart.timestamp), pad(result.authorizationStartTimestamp, 151), infoDiv, PlainTextDiv);
|
|
12767
12805
|
}
|
|
12768
12806
|
{
|
|
12769
|
-
if (HashedPlainTextDiv.parentElement
|
|
12770
|
-
HashedPlainTextDiv.parentElement.children
|
|
12807
|
+
if (HashedPlainTextDiv.parentElement) {
|
|
12808
|
+
getArrayLikeElement(HashedPlainTextDiv.parentElement.children, 0, "Missing hashed plain text header").innerHTML = "Hashed plain text (SHA256, 24 bytes, hex)";
|
|
12771
12809
|
}
|
|
12772
12810
|
HashedPlainTextDiv.innerHTML = result.sha256value?.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
12773
12811
|
}
|
|
12774
12812
|
if (result.publicKey != null && result.publicKey != "") {
|
|
12775
|
-
if (PublicKeyDiv.parentElement
|
|
12776
|
-
PublicKeyDiv.parentElement.children
|
|
12813
|
+
if (PublicKeyDiv.parentElement) {
|
|
12814
|
+
getArrayLikeElement(PublicKeyDiv.parentElement.children, 0, "Missing public key header").innerHTML = "Public Key (" + (result.publicKeyFormat != null && result.publicKeyFormat.length > 0 ? result.publicKeyFormat + ", " : "") + "hex)";
|
|
12777
12815
|
}
|
|
12778
12816
|
if (!IsNullOrEmpty(result.publicKey))
|
|
12779
12817
|
PublicKeyDiv.innerHTML = result.publicKey.startsWith("04") ? '<span class="leadingFour">04</span> ' + (result.publicKey.substring(2).match(/.{1,8}/g)?.join(" ") ?? "") : result.publicKey.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
12780
|
-
|
|
12781
|
-
|
|
12818
|
+
const publicKeySignatureContainer = PublicKeyDiv.parentElement?.children[3];
|
|
12819
|
+
if (publicKeySignatureContainer !== void 0) {
|
|
12820
|
+
publicKeySignatureContainer.innerHTML = "";
|
|
12782
12821
|
}
|
|
12783
12822
|
if (result.publicKeySignatures) {
|
|
12784
12823
|
for (const signature of result.publicKeySignatures) {
|
|
12785
12824
|
try {
|
|
12786
|
-
const signatureDiv =
|
|
12825
|
+
const signatureDiv = publicKeySignatureContainer?.appendChild(document.createElement("div"));
|
|
12787
12826
|
if (signatureDiv) {
|
|
12788
12827
|
signatureDiv.classList.add("signature");
|
|
12789
12828
|
signatureDiv.innerHTML = await this.chargy.CheckMeterPublicKeySignature(
|
|
12790
12829
|
measurementValue.measurement.chargingSession.chargingStation,
|
|
12791
12830
|
measurementValue.measurement.chargingSession.EVSE,
|
|
12792
|
-
measurementValue.measurement.chargingSession.EVSE?.meters
|
|
12793
|
-
measurementValue.measurement.chargingSession.EVSE?.meters
|
|
12831
|
+
measurementValue.measurement.chargingSession.EVSE?.meters.at(0),
|
|
12832
|
+
measurementValue.measurement.chargingSession.EVSE?.meters.at(0)?.publicKeys?.at(0),
|
|
12794
12833
|
signature
|
|
12795
12834
|
);
|
|
12796
12835
|
signatureDiv.onclick = () => {
|
|
@@ -12804,12 +12843,12 @@ var EMHCrypt01 = class extends ACrypt {
|
|
|
12804
12843
|
}
|
|
12805
12844
|
}
|
|
12806
12845
|
if (result.signature != null) {
|
|
12807
|
-
if (SignatureExpectedDiv.parentElement
|
|
12808
|
-
SignatureExpectedDiv.parentElement.children
|
|
12846
|
+
if (SignatureExpectedDiv.parentElement) {
|
|
12847
|
+
getArrayLikeElement(SignatureExpectedDiv.parentElement.children, 0, "Missing expected signature header").innerHTML = "Erwartete Signatur (" + (result.signature.format ?? "") + ", hex)";
|
|
12809
12848
|
}
|
|
12810
|
-
if (result.signature.r && result.signature.s)
|
|
12849
|
+
if (result.signature.r != null && result.signature.r.length > 0 && result.signature.s != null && result.signature.s.length > 0)
|
|
12811
12850
|
SignatureExpectedDiv.innerHTML = "r: " + (result.signature.r.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "") + "<br />s: " + (result.signature.s.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "");
|
|
12812
|
-
else if (result.signature.value)
|
|
12851
|
+
else if (result.signature.value != null && result.signature.value.length > 0)
|
|
12813
12852
|
SignatureExpectedDiv.innerHTML = result.signature.value.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
12814
12853
|
}
|
|
12815
12854
|
{
|
|
@@ -12837,6 +12876,7 @@ var EMHCrypt01 = class extends ACrypt {
|
|
|
12837
12876
|
break;
|
|
12838
12877
|
}
|
|
12839
12878
|
}
|
|
12879
|
+
return void 0;
|
|
12840
12880
|
}
|
|
12841
12881
|
//#region Helper methods
|
|
12842
12882
|
DecodeStatus(statusValue) {
|
|
@@ -12905,7 +12945,7 @@ var GDFCrypt01 = class extends ACrypt {
|
|
|
12905
12945
|
measurementValue.result = cryptoResult;
|
|
12906
12946
|
return cryptoResult;
|
|
12907
12947
|
}
|
|
12908
|
-
if (measurementValue.measurement
|
|
12948
|
+
if (measurementValue.measurement?.chargingSession === void 0) {
|
|
12909
12949
|
return {
|
|
12910
12950
|
status: "InvalidMeasurement" /* InvalidMeasurement */
|
|
12911
12951
|
};
|
|
@@ -12940,9 +12980,10 @@ var GDFCrypt01 = class extends ACrypt {
|
|
|
12940
12980
|
cryptoResult.meter = meter;
|
|
12941
12981
|
if (meter.publicKeys != null && meter.publicKeys.length > 0) {
|
|
12942
12982
|
try {
|
|
12943
|
-
|
|
12944
|
-
cryptoResult.
|
|
12945
|
-
cryptoResult.
|
|
12983
|
+
const publicKey = meter.publicKeys.at(0);
|
|
12984
|
+
cryptoResult.publicKey = publicKey?.value?.toLowerCase();
|
|
12985
|
+
cryptoResult.publicKeyFormat = publicKey?.format;
|
|
12986
|
+
cryptoResult.publicKeySignatures = publicKey?.signatures;
|
|
12946
12987
|
try {
|
|
12947
12988
|
if (this.curve.keyFromPublic(cryptoResult.publicKey ?? "", "hex").verify(
|
|
12948
12989
|
cryptoResult.sha256value,
|
|
@@ -12967,54 +13008,55 @@ var GDFCrypt01 = class extends ACrypt {
|
|
|
12967
13008
|
}
|
|
12968
13009
|
return {};
|
|
12969
13010
|
}
|
|
12970
|
-
async ViewMeasurement(measurementValue,
|
|
12971
|
-
if (measurementValue.measurement
|
|
13011
|
+
async ViewMeasurement(measurementValue, _errorDiv, introDiv, infoDiv, PlainTextDiv, HashedPlainTextDiv, PublicKeyDiv, SignatureExpectedDiv, SignatureCheckDiv) {
|
|
13012
|
+
if (measurementValue.measurement?.chargingSession?.authorizationStart.timestamp === void 0) {
|
|
12972
13013
|
return new Error("Invalid measurement!");
|
|
12973
13014
|
}
|
|
12974
13015
|
const result = measurementValue.result;
|
|
12975
13016
|
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "GDFCrypt01").replace("{cryptoAlgorithm}", this.description);
|
|
12976
13017
|
{
|
|
12977
|
-
if (PlainTextDiv.parentElement
|
|
12978
|
-
PlainTextDiv.parentElement.children
|
|
13018
|
+
if (PlainTextDiv.parentElement) {
|
|
13019
|
+
getArrayLikeElement(PlainTextDiv.parentElement.children, 0, "Missing plain text header").innerHTML = "Plain text (320 Bytes, hex)";
|
|
12979
13020
|
}
|
|
12980
13021
|
PlainTextDiv.style.fontFamily = "";
|
|
12981
13022
|
PlainTextDiv.style.whiteSpace = "";
|
|
12982
13023
|
PlainTextDiv.style.maxHeight = "";
|
|
12983
13024
|
PlainTextDiv.style.overflowY = "";
|
|
12984
|
-
this.CreateLine("Z\xE4hlernummer", measurementValue.measurement.energyMeterId, result.meterId
|
|
12985
|
-
this.CreateLine("Zeitstempel", UTC2human(measurementValue.timestamp), result.timestamp
|
|
12986
|
-
this.CreateLine("OBIS-Kennzahl", measurementValue.measurement.obis, result.obis
|
|
12987
|
-
this.CreateLine("Einheit (codiert)", measurementValue.measurement.unitEncoded ?? 0, result.unitEncoded
|
|
12988
|
-
this.CreateLine("Skalierung", measurementValue.measurement.scale, result.scale
|
|
12989
|
-
this.CreateLine("Messwert", measurementValue.value.toString() + " Wh", result.value
|
|
12990
|
-
this.CreateLine("Autorisierung", measurementValue.measurement.chargingSession.authorizationStart["@id"] + " hex", pad(result.authorizationStart, 128)
|
|
12991
|
-
this.CreateLine("Autorisierungszeitpunkt", UTC2human(measurementValue.measurement.chargingSession.authorizationStart.timestamp), pad(result.authorizationStartTimestamp, 151)
|
|
13025
|
+
this.CreateLine("Z\xE4hlernummer", measurementValue.measurement.energyMeterId, result.meterId ?? "", infoDiv, PlainTextDiv);
|
|
13026
|
+
this.CreateLine("Zeitstempel", UTC2human(measurementValue.timestamp), result.timestamp ?? "", infoDiv, PlainTextDiv);
|
|
13027
|
+
this.CreateLine("OBIS-Kennzahl", measurementValue.measurement.obis, result.obis ?? "", infoDiv, PlainTextDiv);
|
|
13028
|
+
this.CreateLine("Einheit (codiert)", measurementValue.measurement.unitEncoded ?? 0, result.unitEncoded ?? "", infoDiv, PlainTextDiv);
|
|
13029
|
+
this.CreateLine("Skalierung", measurementValue.measurement.scale, result.scale ?? "", infoDiv, PlainTextDiv);
|
|
13030
|
+
this.CreateLine("Messwert", measurementValue.value.toString() + " Wh", result.value ?? "", infoDiv, PlainTextDiv);
|
|
13031
|
+
this.CreateLine("Autorisierung", measurementValue.measurement.chargingSession.authorizationStart["@id"] + " hex", pad(result.authorizationStart, 128), infoDiv, PlainTextDiv);
|
|
13032
|
+
this.CreateLine("Autorisierungszeitpunkt", UTC2human(measurementValue.measurement.chargingSession.authorizationStart.timestamp), pad(result.authorizationStartTimestamp, 151), infoDiv, PlainTextDiv);
|
|
12992
13033
|
}
|
|
12993
13034
|
{
|
|
12994
|
-
if (HashedPlainTextDiv.parentElement
|
|
12995
|
-
HashedPlainTextDiv.parentElement.children
|
|
13035
|
+
if (HashedPlainTextDiv.parentElement) {
|
|
13036
|
+
getArrayLikeElement(HashedPlainTextDiv.parentElement.children, 0, "Missing hashed plain text header").innerHTML = "Hashed plain text (SHA256, 24 bytes, hex)";
|
|
12996
13037
|
}
|
|
12997
13038
|
HashedPlainTextDiv.innerHTML = result.sha256value?.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
12998
13039
|
}
|
|
12999
13040
|
if (result.publicKey != null && result.publicKey != "") {
|
|
13000
|
-
if (PublicKeyDiv.parentElement
|
|
13001
|
-
PublicKeyDiv.parentElement.children
|
|
13041
|
+
if (PublicKeyDiv.parentElement) {
|
|
13042
|
+
getArrayLikeElement(PublicKeyDiv.parentElement.children, 0, "Missing public key header").innerHTML = "Public Key (" + (result.publicKeyFormat != null && result.publicKeyFormat.length > 0 ? result.publicKeyFormat + ", " : "") + "hex)";
|
|
13002
13043
|
}
|
|
13003
13044
|
if (!IsNullOrEmpty(result.publicKey))
|
|
13004
13045
|
PublicKeyDiv.innerHTML = result.publicKey.startsWith("04") ? '<span class="leadingFour">04</span> ' + (result.publicKey.substring(2).match(/.{1,8}/g)?.join(" ") ?? "") : result.publicKey.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
13005
|
-
|
|
13006
|
-
|
|
13046
|
+
const publicKeySignatureContainer = PublicKeyDiv.parentElement?.children[3];
|
|
13047
|
+
if (publicKeySignatureContainer !== void 0) {
|
|
13048
|
+
publicKeySignatureContainer.innerHTML = "";
|
|
13007
13049
|
}
|
|
13008
13050
|
if (result.publicKeySignatures) {
|
|
13009
13051
|
for (const signature of result.publicKeySignatures) {
|
|
13010
13052
|
try {
|
|
13011
|
-
const signatureDiv =
|
|
13053
|
+
const signatureDiv = publicKeySignatureContainer?.appendChild(document.createElement("div"));
|
|
13012
13054
|
if (signatureDiv != null)
|
|
13013
13055
|
signatureDiv.innerHTML = await this.chargy.CheckMeterPublicKeySignature(
|
|
13014
13056
|
measurementValue.measurement.chargingSession.chargingStation,
|
|
13015
13057
|
measurementValue.measurement.chargingSession.EVSE,
|
|
13016
|
-
measurementValue.measurement.chargingSession.EVSE?.meters
|
|
13017
|
-
measurementValue.measurement.chargingSession.EVSE?.meters
|
|
13058
|
+
measurementValue.measurement.chargingSession.EVSE?.meters.at(0),
|
|
13059
|
+
measurementValue.measurement.chargingSession.EVSE?.meters.at(0)?.publicKeys?.at(0),
|
|
13018
13060
|
signature
|
|
13019
13061
|
);
|
|
13020
13062
|
} catch (exception) {
|
|
@@ -13024,12 +13066,12 @@ var GDFCrypt01 = class extends ACrypt {
|
|
|
13024
13066
|
}
|
|
13025
13067
|
}
|
|
13026
13068
|
if (result.signature != null) {
|
|
13027
|
-
if (SignatureExpectedDiv.parentElement
|
|
13028
|
-
SignatureExpectedDiv.parentElement.children
|
|
13069
|
+
if (SignatureExpectedDiv.parentElement) {
|
|
13070
|
+
getArrayLikeElement(SignatureExpectedDiv.parentElement.children, 0, "Missing expected signature header").innerHTML = "Erwartete Signatur (" + (result.signature.format ?? "") + ", hex)";
|
|
13029
13071
|
}
|
|
13030
|
-
if (result.signature.r && result.signature.s)
|
|
13072
|
+
if (result.signature.r != null && result.signature.r.length > 0 && result.signature.s != null && result.signature.s.length > 0)
|
|
13031
13073
|
SignatureExpectedDiv.innerHTML = "r: " + (result.signature.r.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "") + "<br />s: " + (result.signature.s.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "");
|
|
13032
|
-
else if (result.signature.value)
|
|
13074
|
+
else if (result.signature.value != null && result.signature.value.length > 0)
|
|
13033
13075
|
SignatureExpectedDiv.innerHTML = result.signature.value.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "-";
|
|
13034
13076
|
}
|
|
13035
13077
|
{
|
|
@@ -13057,6 +13099,7 @@ var GDFCrypt01 = class extends ACrypt {
|
|
|
13057
13099
|
break;
|
|
13058
13100
|
}
|
|
13059
13101
|
}
|
|
13102
|
+
return void 0;
|
|
13060
13103
|
}
|
|
13061
13104
|
};
|
|
13062
13105
|
var MENNEKES_EDL40_XMLNS = "http://www.mennekes.de/Mennekes.EdlVerification.xsd";
|
|
@@ -13303,7 +13346,7 @@ var MennekesCrypt01 = class extends ACrypt {
|
|
|
13303
13346
|
signedData.byteOffset + signedData.byteLength
|
|
13304
13347
|
);
|
|
13305
13348
|
cryptoResult.hashValue = (await sha256(new DataView(signedDataBuffer))).substring(0, 48);
|
|
13306
|
-
const publicKey = cleanHex(meter.publicKeys
|
|
13349
|
+
const publicKey = cleanHex(meter.publicKeys.at(0)?.value ?? measurement.publicKey);
|
|
13307
13350
|
if (publicKey.length !== 96)
|
|
13308
13351
|
return setResult("InvalidPublicKey" /* InvalidPublicKey */);
|
|
13309
13352
|
const result = this.curve192r1.keyFromPublic("04" + publicKey, "hex").verify(
|
|
@@ -13334,7 +13377,7 @@ var MennekesCrypt01 = class extends ACrypt {
|
|
|
13334
13377
|
PublicKeyDiv.innerHTML = result.publicKey?.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
13335
13378
|
if (SignatureExpectedDiv.parentElement?.children[0])
|
|
13336
13379
|
SignatureExpectedDiv.parentElement.children[0].innerHTML = this.chargy.GetLocalizedMessage("Expected signature") + " (rs, hex)";
|
|
13337
|
-
SignatureExpectedDiv.innerHTML = result.signature?.r && result.signature.s ? "r: " + (result.signature.r.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "") + "<br />s: " + (result.signature.s.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "") : "";
|
|
13380
|
+
SignatureExpectedDiv.innerHTML = result.signature?.r != null && result.signature.r.length > 0 && result.signature.s != null && result.signature.s.length > 0 ? "r: " + (result.signature.r.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "") + "<br />s: " + (result.signature.s.toLowerCase().match(/.{1,8}/g)?.join(" ") ?? "") : "";
|
|
13338
13381
|
SignatureCheckDiv.innerHTML = result.status === "ValidSignature" /* ValidSignature */ ? '<i class="fas fa-check-circle"></i><div id="description">' + this.chargy.GetLocalizedMessage("Valid signature") + "</div>" : '<i class="fas fa-times-circle"></i><div id="description">' + this.chargy.GetLocalizedMessage("Invalid signature") + "</div>";
|
|
13339
13382
|
return Promise.resolve(void 0);
|
|
13340
13383
|
}
|
|
@@ -13353,10 +13396,7 @@ function parseMennekesXMLDocument(XMLDocument) {
|
|
|
13353
13396
|
throw new Error("No Mennekes ChargingProcess element found!");
|
|
13354
13397
|
if (isLocalName(XMLDocument.documentElement, "ChargingProcess")) {
|
|
13355
13398
|
if (chargingProcesses.length > 1) {
|
|
13356
|
-
|
|
13357
|
-
if (chargingProcess != void 0)
|
|
13358
|
-
return chargingProcess;
|
|
13359
|
-
throw new Error("No Mennekes ChargingProcess found!");
|
|
13399
|
+
return getFirstArrayElement(chargingProcesses, "No Mennekes ChargingProcess found!");
|
|
13360
13400
|
}
|
|
13361
13401
|
}
|
|
13362
13402
|
return {
|
|
@@ -13479,8 +13519,8 @@ function toMennekesMeasurementValue(measurement) {
|
|
|
13479
13519
|
}
|
|
13480
13520
|
function validateMennekesLawConformity(measurementValues, chargy) {
|
|
13481
13521
|
const errors = new Array();
|
|
13482
|
-
const start = measurementValues
|
|
13483
|
-
const end = measurementValues
|
|
13522
|
+
const start = measurementValues.at(0);
|
|
13523
|
+
const end = measurementValues.at(-1);
|
|
13484
13524
|
if (start == null || end == null)
|
|
13485
13525
|
return [
|
|
13486
13526
|
CreateError(chargy.GetMultilanguageText("Mennekes EDL40 requires at least two measurement values."))
|
|
@@ -13610,9 +13650,9 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
13610
13650
|
}
|
|
13611
13651
|
async VerifyMeasurement(measurementValue) {
|
|
13612
13652
|
measurementValue.method = this;
|
|
13613
|
-
return {
|
|
13653
|
+
return Promise.resolve({
|
|
13614
13654
|
status: measurementValue.result?.status ?? "Unvalidated" /* Unvalidated */
|
|
13615
|
-
};
|
|
13655
|
+
});
|
|
13616
13656
|
}
|
|
13617
13657
|
PayloadDictionary(Path, Key) {
|
|
13618
13658
|
if (Path === "RD") {
|
|
@@ -13682,7 +13722,7 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
13682
13722
|
return Key;
|
|
13683
13723
|
}
|
|
13684
13724
|
}
|
|
13685
|
-
SignatureDictionary(
|
|
13725
|
+
SignatureDictionary(_Path, Key) {
|
|
13686
13726
|
switch (Key) {
|
|
13687
13727
|
case "SA":
|
|
13688
13728
|
return "Signature Algorithm";
|
|
@@ -13696,16 +13736,16 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
13696
13736
|
return Key;
|
|
13697
13737
|
}
|
|
13698
13738
|
}
|
|
13699
|
-
async ViewMeasurement(measurementValue,
|
|
13739
|
+
async ViewMeasurement(measurementValue, _errorDiv, introDiv, _infoDiv, PlainTextDiv, HashedPlainTextDiv, PublicKeyDiv, SignatureExpectedDiv, SignatureCheckDiv) {
|
|
13700
13740
|
if (!measurementValue.measurement || !measurementValue.ocmfDocument || !measurementValue.measurement.chargingSession) {
|
|
13701
13741
|
return new Error("Invalid measurement!");
|
|
13702
13742
|
}
|
|
13703
13743
|
{
|
|
13704
|
-
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "OCMFCrypt01").replace("{cryptoAlgorithm}", measurementValue.ocmfDocument.publicKey && typeof measurementValue.ocmfDocument.publicKey !== "string" ? "(" + measurementValue.ocmfDocument.publicKey.algorithm + ") " : "");
|
|
13744
|
+
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "OCMFCrypt01").replace("{cryptoAlgorithm}", measurementValue.ocmfDocument.publicKey != null && typeof measurementValue.ocmfDocument.publicKey !== "string" ? "(" + measurementValue.ocmfDocument.publicKey.algorithm + ") " : "");
|
|
13705
13745
|
}
|
|
13706
13746
|
{
|
|
13707
|
-
if (PlainTextDiv.parentElement
|
|
13708
|
-
PlainTextDiv.parentElement.children
|
|
13747
|
+
if (PlainTextDiv.parentElement) {
|
|
13748
|
+
getArrayLikeElement(PlainTextDiv.parentElement.children, 0, "Missing plain text header").innerHTML = this.chargy.GetLocalizedMessage("Plain text") + " (OCMF|<" + this.chargy.GetLocalizedMessage("payload") + ">|<" + this.chargy.GetLocalizedMessage("signature") + ">)";
|
|
13709
13749
|
}
|
|
13710
13750
|
PlainTextDiv.innerText = "";
|
|
13711
13751
|
const compactValueDiv = document.createElement("div");
|
|
@@ -13737,29 +13777,30 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
13737
13777
|
};
|
|
13738
13778
|
}
|
|
13739
13779
|
{
|
|
13740
|
-
if (HashedPlainTextDiv.parentElement
|
|
13741
|
-
HashedPlainTextDiv.parentElement.children
|
|
13780
|
+
if (HashedPlainTextDiv.parentElement) {
|
|
13781
|
+
getArrayLikeElement(HashedPlainTextDiv.parentElement.children, 0, "Missing hashed payload header").innerHTML = this.chargy.GetLocalizedMessage("Hashed payload") + " (" + measurementValue.ocmfDocument.hashAlgorithm + ")";
|
|
13742
13782
|
}
|
|
13743
13783
|
HashedPlainTextDiv.innerHTML = measurementValue.ocmfDocument.hashValue.match(/.{1,8}/g)?.join(" ") ?? "0x00000000000000000000000000000000000";
|
|
13744
13784
|
}
|
|
13745
|
-
if (measurementValue.ocmfDocument.publicKey) {
|
|
13746
|
-
if (PublicKeyDiv.parentElement
|
|
13747
|
-
PublicKeyDiv.parentElement.children
|
|
13785
|
+
if (measurementValue.ocmfDocument.publicKey != null) {
|
|
13786
|
+
if (PublicKeyDiv.parentElement) {
|
|
13787
|
+
getArrayLikeElement(PublicKeyDiv.parentElement.children, 0, "Missing public key header").innerHTML = typeof measurementValue.ocmfDocument.publicKey === "string" ? this.chargy.GetLocalizedMessage("Public Key") : this.chargy.GetLocalizedMessage("Public Key") + " (" + measurementValue.ocmfDocument.publicKey.algorithm + ", " + measurementValue.ocmfDocument.publicKey.encoding + ")";
|
|
13748
13788
|
}
|
|
13749
13789
|
PublicKeyDiv.innerHTML = typeof measurementValue.ocmfDocument.publicKey === "string" ? measurementValue.ocmfDocument.publicKey : "der: " + (measurementValue.ocmfDocument.publicKey.value?.match(/.{1,8}/g)?.join(" ") ?? "-") + "<br /><br />x: " + (measurementValue.ocmfDocument.publicKey.x.match(/.{1,8}/g)?.join(" ") ?? "-") + "<br />y: " + (measurementValue.ocmfDocument.publicKey.y.match(/.{1,8}/g)?.join(" ") ?? "-");
|
|
13750
13790
|
}
|
|
13751
|
-
|
|
13752
|
-
|
|
13791
|
+
const publicKeySignatureContainer = PublicKeyDiv.parentElement?.children[3];
|
|
13792
|
+
if (publicKeySignatureContainer !== void 0) {
|
|
13793
|
+
publicKeySignatureContainer.innerHTML = "";
|
|
13753
13794
|
const result = measurementValue.result;
|
|
13754
13795
|
if (result.publicKeySignatures) {
|
|
13755
13796
|
for (const signature of result.publicKeySignatures) {
|
|
13756
13797
|
try {
|
|
13757
|
-
const signatureDiv =
|
|
13798
|
+
const signatureDiv = publicKeySignatureContainer.appendChild(document.createElement("div"));
|
|
13758
13799
|
signatureDiv.innerHTML = await this.chargy.CheckMeterPublicKeySignature(
|
|
13759
13800
|
measurementValue.measurement.chargingSession.chargingStation,
|
|
13760
13801
|
measurementValue.measurement.chargingSession.EVSE,
|
|
13761
|
-
measurementValue.measurement.chargingSession.EVSE?.meters
|
|
13762
|
-
measurementValue.measurement.chargingSession.EVSE?.meters
|
|
13802
|
+
measurementValue.measurement.chargingSession.EVSE?.meters.at(0),
|
|
13803
|
+
measurementValue.measurement.chargingSession.EVSE?.meters.at(0)?.publicKeys?.at(0),
|
|
13763
13804
|
signature
|
|
13764
13805
|
);
|
|
13765
13806
|
} catch {
|
|
@@ -13769,8 +13810,8 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
13769
13810
|
}
|
|
13770
13811
|
}
|
|
13771
13812
|
if (measurementValue.ocmfDocument.signature.SD) {
|
|
13772
|
-
if (SignatureExpectedDiv.parentElement
|
|
13773
|
-
SignatureExpectedDiv.parentElement.children
|
|
13813
|
+
if (SignatureExpectedDiv.parentElement) {
|
|
13814
|
+
getArrayLikeElement(SignatureExpectedDiv.parentElement.children, 0, "Missing expected signature header").innerHTML = this.chargy.GetLocalizedMessage("Expected signature") + " (rs, hex)";
|
|
13774
13815
|
}
|
|
13775
13816
|
SignatureExpectedDiv.innerHTML = "der: " + (measurementValue.ocmfDocument.signature.SD.match(/.{1,8}/g)?.join(" ") ?? "-") + "<br /><br />r: " + (measurementValue.ocmfDocument.signatureRS?.r?.toLowerCase().padStart(56, "0").match(/.{1,8}/g)?.join(" ") ?? "-") + "<br />s: " + (measurementValue.ocmfDocument.signatureRS?.s?.toLowerCase().padStart(56, "0").match(/.{1,8}/g)?.join(" ") ?? "-");
|
|
13776
13817
|
}
|
|
@@ -13793,6 +13834,7 @@ var OCMFv1_x = class extends ACrypt {
|
|
|
13793
13834
|
break;
|
|
13794
13835
|
}
|
|
13795
13836
|
}
|
|
13837
|
+
return void 0;
|
|
13796
13838
|
}
|
|
13797
13839
|
};
|
|
13798
13840
|
var OCMFTransactionTypes = /* @__PURE__ */ ((OCMFTransactionTypes2) => {
|
|
@@ -13825,268 +13867,270 @@ var OCMF = class {
|
|
|
13825
13867
|
const evseId = asString(containerInfos["EVSEId"]) ?? asString(evseInfo?.["@id"]) ?? "DE*GEF*EVSE*CHARGY*1";
|
|
13826
13868
|
const stationDescription = asJSONObject(chargingStationInfo?.["description"]);
|
|
13827
13869
|
const evseDescription = asJSONObject(evseInfo?.["description"]);
|
|
13828
|
-
const firstOCMDJSONDocument = OCMFJSONDocuments
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
13842
|
-
|
|
13843
|
-
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
|
|
13877
|
-
|
|
13878
|
-
|
|
13879
|
-
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
|
|
13870
|
+
const firstOCMDJSONDocument = getFirstArrayElement(OCMFJSONDocuments, "Missing first OCMF JSON document");
|
|
13871
|
+
const formatVersion = firstOCMDJSONDocument.payload.FV;
|
|
13872
|
+
const gatewayInformation = firstOCMDJSONDocument.payload.GI ?? firstOCMDJSONDocument.payload.VI;
|
|
13873
|
+
const gatewaySerial = firstOCMDJSONDocument.payload.GS;
|
|
13874
|
+
const gatewayVersion = firstOCMDJSONDocument.payload.GV ?? firstOCMDJSONDocument.payload.VV;
|
|
13875
|
+
const paging = firstOCMDJSONDocument.payload.PG;
|
|
13876
|
+
const meterVendor = firstOCMDJSONDocument.payload.MV;
|
|
13877
|
+
const meterModel = firstOCMDJSONDocument.payload.MM;
|
|
13878
|
+
const meterSerial = firstOCMDJSONDocument.payload.MS;
|
|
13879
|
+
const effectiveMeterSerial = meterSerial ?? gatewaySerial;
|
|
13880
|
+
const meterFirmware = firstOCMDJSONDocument.payload.MF;
|
|
13881
|
+
const identificationStatus = firstOCMDJSONDocument.payload.IS;
|
|
13882
|
+
const identificationLevel = firstOCMDJSONDocument.payload.IL;
|
|
13883
|
+
const identificationFlags = firstOCMDJSONDocument.payload.IF;
|
|
13884
|
+
const identificationType = firstOCMDJSONDocument.payload.IT;
|
|
13885
|
+
const identificationData = firstOCMDJSONDocument.payload.ID;
|
|
13886
|
+
const tariffText = firstOCMDJSONDocument.payload.TT;
|
|
13887
|
+
const controlerFirmwareVersion = firstOCMDJSONDocument.payload.CF;
|
|
13888
|
+
const lossCompensation = firstOCMDJSONDocument.payload.LC;
|
|
13889
|
+
const chargePointIdType = firstOCMDJSONDocument.payload.CT;
|
|
13890
|
+
const chargePointId = firstOCMDJSONDocument.payload.CI;
|
|
13891
|
+
if (isOptionalString(formatVersion) && isOptionalString(gatewayInformation) && isOptionalString(gatewaySerial) && isOptionalString(gatewayVersion) && isMandatoryString(paging) && isOptionalString(meterVendor) && isOptionalString(meterModel) && // OCMF 1.0 table 3 lists MS as 1..1, but the later "Relation of Serial Numbers,
|
|
13892
|
+
// Charge Point and Public Key" section makes the serial-number fields conditionally
|
|
13893
|
+
// mandatory. KEBA KCP30 records identify the signing gateway via GS and omit MS.
|
|
13894
|
+
// Therefore we keep meterSerial and gatewaySerial separate, but require at least one
|
|
13895
|
+
// usable serial number for the CTR meter identity.
|
|
13896
|
+
isMandatoryString(effectiveMeterSerial) && isOptionalString(meterFirmware) && // OCMF 1.x uses boolean IS values. OCMF 0.1 SAFE reference data used status strings
|
|
13897
|
+
// such as "VERIFIED", so accept both forms and preserve the original value in the CTR.
|
|
13898
|
+
(isMandatoryBoolean(identificationStatus) || isMandatoryString(identificationStatus)) && isOptionalString(identificationLevel) && //chargyLib.isMandatoryArrayOfStrings (identificationFlags) &&
|
|
13899
|
+
isOptionalArrayOfStrings(identificationFlags) && // Note: Some vendors do not use this MANDATORY field!
|
|
13900
|
+
// IT is 1..1 in the user-assignment table for transaction records. Some OCMF 1.0
|
|
13901
|
+
// implementations omit it when no user is assigned (IS=false, ID empty). We tolerate
|
|
13902
|
+
// this vendor compatibility case instead of rejecting otherwise valid signatures.
|
|
13903
|
+
isOptionalString(identificationType) && isOptionalString(identificationData) && isOptionalString(tariffText) && isOptionalString(controlerFirmwareVersion) && isOptionalJSONObject(lossCompensation) && isOptionalString(chargePointIdType) && isOptionalString(chargePointId)) {
|
|
13904
|
+
const paginationPrefix = paging.length > 0 ? paging.charAt(0).toLowerCase() : null;
|
|
13905
|
+
const transactionType = paginationPrefix === "t" ? "transaction" /* transaction */ : paginationPrefix === "f" ? "fiscal" /* fiscal */ : "undefined" /* undefined */;
|
|
13906
|
+
const pagination = paging.length > 1 ? parseNumber(paging.substring(1)) : null;
|
|
13907
|
+
if (transactionType === "undefined" /* undefined */) return {
|
|
13908
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
13909
|
+
message: this.chargy.GetMultilanguageText("The given OCMF data does not have a valid transaction type!"),
|
|
13910
|
+
certainty: 0
|
|
13911
|
+
};
|
|
13912
|
+
if (!isMandatoryNumber(pagination)) return {
|
|
13913
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
13914
|
+
message: this.chargy.GetMultilanguageText("The given OCMF data does not have a valid pagination counter!"),
|
|
13915
|
+
certainty: 0
|
|
13916
|
+
};
|
|
13917
|
+
if (firstOCMDJSONDocument.payload.RD.length == 0) return {
|
|
13918
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
13919
|
+
message: this.chargy.GetMultilanguageText("Each OCMF data set must have at least one meter reading!"),
|
|
13920
|
+
certainty: 0
|
|
13921
|
+
};
|
|
13922
|
+
const CTR = {
|
|
13923
|
+
"@id": "?",
|
|
13924
|
+
"@context": "https://open.charging.cloud/contexts/CTR+json",
|
|
13925
|
+
//"@context": [ "https://open.charging.cloud/contexts/CTR+json", "https://open.charging.cloud/contexts/CTR_OCMF+json" ],
|
|
13926
|
+
"begin": "?",
|
|
13927
|
+
"end": "?",
|
|
13928
|
+
"description": {
|
|
13929
|
+
"de": "Alle OCMF-Ladevorg\xE4nge"
|
|
13930
|
+
},
|
|
13931
|
+
"contract": {
|
|
13932
|
+
"@id": identificationData ?? "?",
|
|
13933
|
+
"type": identificationType ?? "?"
|
|
13934
|
+
},
|
|
13935
|
+
"ocmf": {
|
|
13936
|
+
"formatVersion": formatVersion,
|
|
13937
|
+
"gatewayInformation": gatewayInformation,
|
|
13938
|
+
"gatewaySerial": gatewaySerial,
|
|
13939
|
+
"gatewayVersion": gatewayVersion
|
|
13940
|
+
},
|
|
13941
|
+
// "chargingStationOperators": [{
|
|
13942
|
+
"chargingPools": [{
|
|
13943
|
+
"@id": "DE*GEF*POOL*CHARGY*1",
|
|
13944
|
+
"description": { "en": "GraphDefined CHARGY Virtual Charging Pool 1" },
|
|
13945
|
+
"chargingStations": [{
|
|
13946
|
+
"@id": chargingStationId,
|
|
13947
|
+
"description": stationDescription && Object.keys(stationDescription).length > 0 ? stationDescription : { "en": "GraphDefined CHARGY Virtual Charging Station 1" },
|
|
13948
|
+
"firmwareVersion": asString(chargingStationInfo?.["softwareVersion"]) ?? asString(chargingStationInfo?.["firmwareVersion"]),
|
|
13949
|
+
"geoLocation": asJSONObject(chargingStationInfo?.["geoLocation"]),
|
|
13950
|
+
"address": asJSONObject(chargingStationInfo?.["address"]),
|
|
13951
|
+
"EVSEs": [{
|
|
13952
|
+
"@id": evseId,
|
|
13953
|
+
"description": evseDescription && Object.keys(evseDescription).length > 0 ? evseDescription : { "en": "GraphDefined CHARGY Virtual EVSE 1" },
|
|
13954
|
+
"connectors": connectorInfo ? [{
|
|
13955
|
+
"type": asString(connectorInfo["type"]) ?? "",
|
|
13956
|
+
"looses": asNumber(connectorInfo["looses"]) ?? 0
|
|
13957
|
+
}] : void 0,
|
|
13958
|
+
"meters": [{
|
|
13959
|
+
"@id": effectiveMeterSerial,
|
|
13960
|
+
// The signed OCMF payload values always win,
|
|
13961
|
+
// the container infos only fill the gaps!
|
|
13962
|
+
"manufacturer": meterVendor ?? asString(energyMeterInfo?.["manufacturer"]),
|
|
13963
|
+
"manufacturerURL": asString(energyMeterInfo?.["manufacturerURL"]),
|
|
13964
|
+
"model": meterModel ?? asString(energyMeterInfo?.["model"]),
|
|
13965
|
+
"modelURL": asString(energyMeterInfo?.["modelURL"]),
|
|
13966
|
+
"hardwareVersion": asString(energyMeterInfo?.["hardwareVersion"]),
|
|
13967
|
+
"firmwareVersion": meterFirmware ?? asString(energyMeterInfo?.["firmwareVersion"]),
|
|
13968
|
+
"signatureFormat": "https://open.charging.cloud/contexts/EnergyMeterSignatureFormats/OCMFv1.0+json",
|
|
13969
|
+
"publicKeys": [{
|
|
13970
|
+
"algorithm": "?",
|
|
13971
|
+
"encoding": "?",
|
|
13972
|
+
"format": "?",
|
|
13973
|
+
"value": "?"
|
|
13974
|
+
}]
|
|
13975
|
+
}]
|
|
13976
|
+
}]
|
|
13977
|
+
}]
|
|
13978
|
+
}],
|
|
13979
|
+
"chargingSessions": [{
|
|
13980
|
+
"@id": "1554181214441:-1965658344385548683:2",
|
|
13981
|
+
"@context": "https://open.charging.cloud/contexts/SessionSignatureFormats/OCMFv1.0+json",
|
|
13885
13982
|
"begin": "?",
|
|
13886
13983
|
"end": "?",
|
|
13887
|
-
"
|
|
13888
|
-
|
|
13889
|
-
},
|
|
13890
|
-
"contract": {
|
|
13984
|
+
"EVSEId": evseId,
|
|
13985
|
+
"authorizationStart": {
|
|
13891
13986
|
"@id": identificationData ?? "?",
|
|
13892
|
-
"type": identificationType ?? "?"
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
"
|
|
13896
|
-
|
|
13897
|
-
"gatewaySerial": gatewaySerial,
|
|
13898
|
-
"gatewayVersion": gatewayVersion
|
|
13987
|
+
"type": identificationType ?? "?",
|
|
13988
|
+
"identificationStatus": identificationStatus,
|
|
13989
|
+
"identificationLevel": identificationLevel,
|
|
13990
|
+
"identificationFlags": identificationFlags ?? []
|
|
13991
|
+
// Note: The OCMF documentation expects an empty array!
|
|
13899
13992
|
},
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13916
|
-
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
|
|
13925
|
-
|
|
13926
|
-
|
|
13927
|
-
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
"
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
const time = effectiveReading.TM;
|
|
13962
|
-
const transaction = effectiveReading.TX;
|
|
13963
|
-
const readingValue = effectiveReading.RV;
|
|
13964
|
-
const readingIdentification = effectiveReading.RI;
|
|
13965
|
-
const readingUnit = effectiveReading.RU;
|
|
13966
|
-
const readingCurrentType = effectiveReading.RT;
|
|
13967
|
-
const cumulatedLoss = effectiveReading.CL;
|
|
13968
|
-
const errorFlags = effectiveReading.EF;
|
|
13969
|
-
const status = effectiveReading.ST;
|
|
13970
|
-
inheritedReading = effectiveReading;
|
|
13971
|
-
if (isMandatoryString(time) && isOptionalString(transaction) && isMandatoryDecimal(readingValue) && // Note: Some vendors use a JSON string here!
|
|
13972
|
-
isOptionalString(readingIdentification) && isMandatoryString(readingUnit) && isOptionalString(readingCurrentType) && isOptionalDecimal(cumulatedLoss) && isOptionalString(errorFlags) && isMandatoryString(status)) {
|
|
13973
|
-
const timeSplit = time.split(" ");
|
|
13974
|
-
if (timeSplit.length != 2) return {
|
|
13975
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
13976
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time format!"),
|
|
13977
|
-
certainty: 0
|
|
13978
|
-
};
|
|
13979
|
-
const timeRegEx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2},\d{3}[+-]\d{4}$/;
|
|
13980
|
-
const timeStamp = timeSplit[0];
|
|
13981
|
-
if (!isMandatoryString(timeStamp) || !timeRegEx.test(timeStamp)) return {
|
|
13982
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
13983
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time sync format!"),
|
|
13984
|
-
certainty: 0
|
|
13985
|
-
};
|
|
13986
|
-
const timeStampISO8601 = this.convertToISO8601(timeStamp);
|
|
13987
|
-
const timeSyncText = timeSplit[1];
|
|
13988
|
-
let timeSync = "unknown" /* unknown */;
|
|
13989
|
-
if (!isMandatoryString(timeSyncText) || !["U", "I", "S", "R"].includes(timeSyncText)) return {
|
|
13990
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
13991
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time sync format!"),
|
|
13992
|
-
certainty: 0
|
|
13993
|
-
};
|
|
13994
|
-
switch (timeSyncText) {
|
|
13995
|
-
case "U":
|
|
13996
|
-
timeSync = "unknown" /* unknown */;
|
|
13997
|
-
break;
|
|
13998
|
-
case "I":
|
|
13999
|
-
timeSync = "informative" /* informative */;
|
|
14000
|
-
break;
|
|
14001
|
-
case "S":
|
|
14002
|
-
timeSync = "syncronized" /* syncronized */;
|
|
14003
|
-
break;
|
|
14004
|
-
case "R":
|
|
14005
|
-
timeSync = "relative" /* relative */;
|
|
14006
|
-
break;
|
|
14007
|
-
default:
|
|
14008
|
-
return {
|
|
14009
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14010
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time sync format!"),
|
|
14011
|
-
certainty: 0
|
|
14012
|
-
};
|
|
14013
|
-
}
|
|
14014
|
-
if (transaction && !["B", "C", "X", "E", "L", "R", "A", "P", "S", "T"].includes(transaction)) return {
|
|
14015
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14016
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid transaction type!"),
|
|
14017
|
-
certainty: 0
|
|
14018
|
-
};
|
|
14019
|
-
if (!["kWh", "Wh", "mOhm", "uOhm"].includes(readingUnit)) return {
|
|
14020
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14021
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid current type!"),
|
|
14022
|
-
certainty: 0
|
|
14023
|
-
};
|
|
14024
|
-
if (readingCurrentType && !["AC", "DC"].includes(readingCurrentType)) return {
|
|
14025
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14026
|
-
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid current type!"),
|
|
14027
|
-
certainty: 0
|
|
14028
|
-
};
|
|
14029
|
-
if (CTR.chargingSessions?.[0]?.begin === "?")
|
|
14030
|
-
CTR.chargingSessions[0].begin = timeStampISO8601;
|
|
14031
|
-
CTR.chargingSessions[0].end = timeStampISO8601;
|
|
14032
|
-
const measurementKey = [
|
|
14033
|
-
readingIdentification ?? "?",
|
|
14034
|
-
readingUnit,
|
|
14035
|
-
readingCurrentType ?? ""
|
|
14036
|
-
].join("|");
|
|
14037
|
-
let measurement = measurementsByKey.get(measurementKey);
|
|
14038
|
-
if (measurement == null) {
|
|
14039
|
-
measurement = {
|
|
14040
|
-
"name": OBIS2MeasurementName(readingIdentification ?? ""),
|
|
14041
|
-
"scale": 0,
|
|
14042
|
-
"energyMeterId": effectiveMeterSerial,
|
|
14043
|
-
"@context": "https://open.charging.cloud/contexts/EnergyMeterSignatureFormats/OCMFv1.0+json",
|
|
14044
|
-
"obis": readingIdentification ?? "?",
|
|
14045
|
-
// OBIS: "1-b:1.8.0"
|
|
14046
|
-
"unit": readingUnit,
|
|
14047
|
-
// "kWh"
|
|
14048
|
-
"currentType": readingCurrentType,
|
|
14049
|
-
// "AC"
|
|
14050
|
-
"values": []
|
|
13993
|
+
"measurements": []
|
|
13994
|
+
}],
|
|
13995
|
+
"certainty": 1
|
|
13996
|
+
};
|
|
13997
|
+
const measurementsByKey = /* @__PURE__ */ new Map();
|
|
13998
|
+
for (const ocmfJSONDocument of OCMFJSONDocuments) {
|
|
13999
|
+
let inheritedReading = {};
|
|
14000
|
+
for (const reading of ocmfJSONDocument.payload.RD) {
|
|
14001
|
+
const effectiveReading = { ...inheritedReading, ...reading };
|
|
14002
|
+
const time = effectiveReading.TM;
|
|
14003
|
+
const transaction = effectiveReading.TX;
|
|
14004
|
+
const readingValue = effectiveReading.RV;
|
|
14005
|
+
const readingIdentification = effectiveReading.RI;
|
|
14006
|
+
const readingUnit = effectiveReading.RU;
|
|
14007
|
+
const readingCurrentType = effectiveReading.RT;
|
|
14008
|
+
const cumulatedLoss = effectiveReading.CL;
|
|
14009
|
+
const errorFlags = effectiveReading.EF;
|
|
14010
|
+
const status = effectiveReading.ST;
|
|
14011
|
+
inheritedReading = effectiveReading;
|
|
14012
|
+
if (isMandatoryString(time) && isOptionalString(transaction) && isMandatoryDecimal(readingValue) && // Note: Some vendors use a JSON string here!
|
|
14013
|
+
isOptionalString(readingIdentification) && isMandatoryString(readingUnit) && isOptionalString(readingCurrentType) && // chargyLib.isOptionalDecimal (cumulatedLoss) &&
|
|
14014
|
+
isOptionalString(errorFlags) && isMandatoryString(status)) {
|
|
14015
|
+
const timeSplit = time.split(" ");
|
|
14016
|
+
if (timeSplit.length != 2) return {
|
|
14017
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14018
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time format!"),
|
|
14019
|
+
certainty: 0
|
|
14020
|
+
};
|
|
14021
|
+
const timeRegEx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2},\d{3}[+-]\d{4}$/;
|
|
14022
|
+
const timeStamp = timeSplit[0];
|
|
14023
|
+
if (!isMandatoryString(timeStamp) || !timeRegEx.test(timeStamp)) return {
|
|
14024
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14025
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time sync format!"),
|
|
14026
|
+
certainty: 0
|
|
14027
|
+
};
|
|
14028
|
+
const timeStampISO8601 = this.convertToISO8601(timeStamp);
|
|
14029
|
+
const timeSyncText = timeSplit[1];
|
|
14030
|
+
let timeSync = "unknown" /* unknown */;
|
|
14031
|
+
if (!isMandatoryString(timeSyncText) || !["U", "I", "S", "R"].includes(timeSyncText)) return {
|
|
14032
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14033
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time sync format!"),
|
|
14034
|
+
certainty: 0
|
|
14035
|
+
};
|
|
14036
|
+
switch (timeSyncText) {
|
|
14037
|
+
case "U":
|
|
14038
|
+
timeSync = "unknown" /* unknown */;
|
|
14039
|
+
break;
|
|
14040
|
+
case "I":
|
|
14041
|
+
timeSync = "informative" /* informative */;
|
|
14042
|
+
break;
|
|
14043
|
+
case "S":
|
|
14044
|
+
timeSync = "syncronized" /* syncronized */;
|
|
14045
|
+
break;
|
|
14046
|
+
case "R":
|
|
14047
|
+
timeSync = "relative" /* relative */;
|
|
14048
|
+
break;
|
|
14049
|
+
default:
|
|
14050
|
+
return {
|
|
14051
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14052
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid time sync format!"),
|
|
14053
|
+
certainty: 0
|
|
14051
14054
|
};
|
|
14052
|
-
measurementsByKey.set(measurementKey, measurement);
|
|
14053
|
-
CTR.chargingSessions[0].measurements.push(measurement);
|
|
14054
|
-
}
|
|
14055
|
-
measurement.values.push({
|
|
14056
|
-
"timestamp": timeStampISO8601,
|
|
14057
|
-
// "2019-06-26T08:57:44,337+0000"
|
|
14058
|
-
"timeSync": timeSync,
|
|
14059
|
-
// "U"|"I"|"S"|"R"
|
|
14060
|
-
"transaction": transaction,
|
|
14061
|
-
// "B"|"C"|"X"|"E"|"L"|"R"|"A"|"P"|"S"|"T"|null
|
|
14062
|
-
"value": new Decimal(readingValue),
|
|
14063
|
-
// 2935.6
|
|
14064
|
-
"transactionType": transactionType,
|
|
14065
|
-
// "T" ToDo: Serialize this to a string!
|
|
14066
|
-
"pagination": pagination,
|
|
14067
|
-
// "9289"
|
|
14068
|
-
"errorFlags": errorFlags,
|
|
14069
|
-
// ""
|
|
14070
|
-
"cumulatedLoss": cumulatedLoss ? new Decimal(cumulatedLoss) : void 0,
|
|
14071
|
-
"status": status,
|
|
14072
|
-
// "G"
|
|
14073
|
-
// "signatures": [{
|
|
14074
|
-
// "value": ocmfJSONDocument.signature["SD"]
|
|
14075
|
-
// }],
|
|
14076
|
-
"result": {
|
|
14077
|
-
"status": ocmfJSONDocument.validationStatus ?? "Unvalidated" /* Unvalidated */
|
|
14078
|
-
},
|
|
14079
|
-
"ocmfDocument": ocmfJSONDocument
|
|
14080
|
-
});
|
|
14081
14055
|
}
|
|
14056
|
+
if (transaction != null && transaction.length > 0 && !["B", "C", "X", "E", "L", "R", "A", "P", "S", "T"].includes(transaction)) return {
|
|
14057
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14058
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid transaction type!"),
|
|
14059
|
+
certainty: 0
|
|
14060
|
+
};
|
|
14061
|
+
if (!["kWh", "Wh", "mOhm", "uOhm"].includes(readingUnit)) return {
|
|
14062
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14063
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid current type!"),
|
|
14064
|
+
certainty: 0
|
|
14065
|
+
};
|
|
14066
|
+
if (readingCurrentType != null && readingCurrentType.length > 0 && !["AC", "DC"].includes(readingCurrentType)) return {
|
|
14067
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
14068
|
+
message: this.chargy.GetMultilanguageText("The given OCMF meter value does not have a valid current type!"),
|
|
14069
|
+
certainty: 0
|
|
14070
|
+
};
|
|
14071
|
+
if (CTR.chargingSessions?.[0]?.begin === "?")
|
|
14072
|
+
CTR.chargingSessions[0].begin = timeStampISO8601;
|
|
14073
|
+
if (CTR.chargingSessions?.[0] !== void 0)
|
|
14074
|
+
CTR.chargingSessions[0].end = timeStampISO8601;
|
|
14075
|
+
const measurementKey = [
|
|
14076
|
+
readingIdentification ?? "?",
|
|
14077
|
+
readingUnit,
|
|
14078
|
+
readingCurrentType ?? ""
|
|
14079
|
+
].join("|");
|
|
14080
|
+
let measurement = measurementsByKey.get(measurementKey);
|
|
14081
|
+
if (measurement == null) {
|
|
14082
|
+
measurement = {
|
|
14083
|
+
"name": OBIS2MeasurementName(readingIdentification ?? ""),
|
|
14084
|
+
"scale": 0,
|
|
14085
|
+
"energyMeterId": effectiveMeterSerial,
|
|
14086
|
+
"@context": "https://open.charging.cloud/contexts/EnergyMeterSignatureFormats/OCMFv1.0+json",
|
|
14087
|
+
"obis": readingIdentification ?? "?",
|
|
14088
|
+
// OBIS: "1-b:1.8.0"
|
|
14089
|
+
"unit": readingUnit,
|
|
14090
|
+
// "kWh"
|
|
14091
|
+
"currentType": readingCurrentType,
|
|
14092
|
+
// "AC"
|
|
14093
|
+
"values": []
|
|
14094
|
+
};
|
|
14095
|
+
measurementsByKey.set(measurementKey, measurement);
|
|
14096
|
+
const firstChargingSession = CTR.chargingSessions?.[0];
|
|
14097
|
+
if (firstChargingSession !== void 0)
|
|
14098
|
+
firstChargingSession.measurements.push(measurement);
|
|
14099
|
+
}
|
|
14100
|
+
measurement.values.push({
|
|
14101
|
+
"timestamp": timeStampISO8601,
|
|
14102
|
+
// "2019-06-26T08:57:44,337+0000"
|
|
14103
|
+
"timeSync": timeSync,
|
|
14104
|
+
// "U"|"I"|"S"|"R"
|
|
14105
|
+
"transaction": transaction,
|
|
14106
|
+
// "B"|"C"|"X"|"E"|"L"|"R"|"A"|"P"|"S"|"T"|null
|
|
14107
|
+
"value": new Decimal(readingValue),
|
|
14108
|
+
// 2935.6
|
|
14109
|
+
"transactionType": transactionType,
|
|
14110
|
+
// "T" ToDo: Serialize this to a string!
|
|
14111
|
+
"pagination": pagination,
|
|
14112
|
+
// "9289"
|
|
14113
|
+
"errorFlags": errorFlags,
|
|
14114
|
+
// ""
|
|
14115
|
+
"cumulatedLoss": cumulatedLoss != null && cumulatedLoss !== 0 ? new Decimal(cumulatedLoss) : void 0,
|
|
14116
|
+
"status": status,
|
|
14117
|
+
// "G"
|
|
14118
|
+
// "signatures": [{
|
|
14119
|
+
// "value": ocmfJSONDocument.signature["SD"]
|
|
14120
|
+
// }],
|
|
14121
|
+
"result": {
|
|
14122
|
+
"status": ocmfJSONDocument.validationStatus ?? "Unvalidated" /* Unvalidated */
|
|
14123
|
+
},
|
|
14124
|
+
"ocmfDocument": ocmfJSONDocument
|
|
14125
|
+
});
|
|
14082
14126
|
}
|
|
14083
14127
|
}
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14128
|
+
}
|
|
14129
|
+
CTR.status = OCMFJSONDocuments.every((ocmfJSONDocument) => ocmfJSONDocument.validationStatus === "ValidSignature" /* ValidSignature */) ? "ValidSignature" /* ValidSignature */ : "InvalidSignature" /* InvalidSignature */;
|
|
14130
|
+
if (CTR.chargingSessions != null && CTR.chargingSessions.length > 0 && CTR.chargingSessions[0]) {
|
|
14131
|
+
CTR.begin = CTR.chargingSessions[0].begin;
|
|
14132
|
+
CTR.end = CTR.chargingSessions[0].end;
|
|
14133
|
+
return CTR;
|
|
14090
14134
|
}
|
|
14091
14135
|
}
|
|
14092
14136
|
} catch (exception) {
|
|
@@ -14103,113 +14147,121 @@ var OCMF = class {
|
|
|
14103
14147
|
}
|
|
14104
14148
|
//#endregion
|
|
14105
14149
|
//#region (private) mergeOCMFSessions(CTRs)
|
|
14106
|
-
mergeOCMFSessions(CTRs)
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14150
|
-
|
|
14151
|
-
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14156
|
-
|
|
14157
|
-
|
|
14150
|
+
// private mergeOCMFSessions(CTRs: Array<IOCMFChargeTransparencyRecord>): IOCMFChargeTransparencyRecord
|
|
14151
|
+
// {
|
|
14152
|
+
// const mergedCTR:IOCMFChargeTransparencyRecord = {
|
|
14153
|
+
// "@id": "",
|
|
14154
|
+
// "@context": "",
|
|
14155
|
+
// certainty: 1
|
|
14156
|
+
// };
|
|
14157
|
+
// for (const ctr of CTRs)
|
|
14158
|
+
// {
|
|
14159
|
+
// if (mergedCTR["@id"] === "")
|
|
14160
|
+
// mergedCTR["@id"] = ctr["@id"];
|
|
14161
|
+
// if (mergedCTR["@context"] === "")
|
|
14162
|
+
// mergedCTR["@context"] = ctr["@context"];
|
|
14163
|
+
// if (!mergedCTR.begin || (mergedCTR.begin && ctr.begin && mergedCTR.begin > ctr.begin))
|
|
14164
|
+
// mergedCTR.begin = ctr.begin;
|
|
14165
|
+
// if (!mergedCTR.end || (mergedCTR.end && ctr.end && mergedCTR.end < ctr.end))
|
|
14166
|
+
// mergedCTR.end = ctr.end;
|
|
14167
|
+
// if (!mergedCTR.description)
|
|
14168
|
+
// mergedCTR.description = ctr.description;
|
|
14169
|
+
// //ToDo: Is this a really good idea? Or should we fail, whenever this information is different?
|
|
14170
|
+
// if (!mergedCTR.contract)
|
|
14171
|
+
// mergedCTR.contract = ctr.contract;
|
|
14172
|
+
// if (!mergedCTR.chargingStationOperators)
|
|
14173
|
+
// mergedCTR.chargingStationOperators = ctr.chargingStationOperators;
|
|
14174
|
+
// else if (ctr.chargingStationOperators)
|
|
14175
|
+
// for (const chargingStationOperator of ctr.chargingStationOperators)
|
|
14176
|
+
// mergedCTR.chargingStationOperators.push(chargingStationOperator);
|
|
14177
|
+
// if (!mergedCTR.chargingPools)
|
|
14178
|
+
// mergedCTR.chargingPools = ctr.chargingPools;
|
|
14179
|
+
// else if (ctr.chargingPools)
|
|
14180
|
+
// for (const chargingPool of ctr.chargingPools)
|
|
14181
|
+
// mergedCTR.chargingPools.push(chargingPool);
|
|
14182
|
+
// if (!mergedCTR.chargingStations)
|
|
14183
|
+
// mergedCTR.chargingStations = ctr.chargingStations;
|
|
14184
|
+
// else if (ctr.chargingStations)
|
|
14185
|
+
// for (const chargingStation of ctr.chargingStations)
|
|
14186
|
+
// mergedCTR.chargingStations.push(chargingStation);
|
|
14187
|
+
// // publicKeys
|
|
14188
|
+
// if (!mergedCTR.chargingSessions)
|
|
14189
|
+
// mergedCTR.chargingSessions = ctr.chargingSessions;
|
|
14190
|
+
// else if (ctr.chargingSessions)
|
|
14191
|
+
// for (const chargingSession of ctr.chargingSessions)
|
|
14192
|
+
// mergedCTR.chargingSessions.push(chargingSession);
|
|
14193
|
+
// if (!mergedCTR.eMobilityProviders)
|
|
14194
|
+
// mergedCTR.eMobilityProviders = ctr.eMobilityProviders;
|
|
14195
|
+
// else if (ctr.eMobilityProviders)
|
|
14196
|
+
// for (const eMobilityProvider of ctr.eMobilityProviders)
|
|
14197
|
+
// mergedCTR.eMobilityProviders.push(eMobilityProvider);
|
|
14198
|
+
// if (!mergedCTR.mediationServices)
|
|
14199
|
+
// mergedCTR.mediationServices = ctr.mediationServices;
|
|
14200
|
+
// else if (ctr.mediationServices)
|
|
14201
|
+
// for (const mediationService of ctr.mediationServices)
|
|
14202
|
+
// mergedCTR.mediationServices.push(mediationService);
|
|
14203
|
+
// }
|
|
14204
|
+
// return mergedCTR;
|
|
14205
|
+
// }
|
|
14158
14206
|
//#endregion
|
|
14159
14207
|
//#region (private) mergeChargeTransparencyRecords(CTRs)
|
|
14160
|
-
mergeChargeTransparencyRecords(CTRs)
|
|
14161
|
-
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
|
|
14179
|
-
|
|
14180
|
-
|
|
14181
|
-
|
|
14182
|
-
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14188
|
-
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
|
|
14211
|
-
|
|
14212
|
-
|
|
14208
|
+
// private mergeChargeTransparencyRecords(CTRs: Array<IOCMFChargeTransparencyRecord>): IOCMFChargeTransparencyRecord
|
|
14209
|
+
// {
|
|
14210
|
+
// const mergedCTR:IOCMFChargeTransparencyRecord = {
|
|
14211
|
+
// "@id": "",
|
|
14212
|
+
// "@context": "",
|
|
14213
|
+
// certainty: 1
|
|
14214
|
+
// };
|
|
14215
|
+
// for (const ctr of CTRs)
|
|
14216
|
+
// {
|
|
14217
|
+
// if (mergedCTR["@id"] === "")
|
|
14218
|
+
// mergedCTR["@id"] = ctr["@id"];
|
|
14219
|
+
// if (mergedCTR["@context"] === "")
|
|
14220
|
+
// mergedCTR["@context"] = ctr["@context"];
|
|
14221
|
+
// if (!mergedCTR.begin || (mergedCTR.begin && ctr.begin && mergedCTR.begin > ctr.begin))
|
|
14222
|
+
// mergedCTR.begin = ctr.begin;
|
|
14223
|
+
// if (!mergedCTR.end || (mergedCTR.end && ctr.end && mergedCTR.end < ctr.end))
|
|
14224
|
+
// mergedCTR.end = ctr.end;
|
|
14225
|
+
// if (!mergedCTR.description)
|
|
14226
|
+
// mergedCTR.description = ctr.description;
|
|
14227
|
+
// //ToDo: Is this a really good idea? Or should we fail, whenever this information is different?
|
|
14228
|
+
// if (!mergedCTR.contract)
|
|
14229
|
+
// mergedCTR.contract = ctr.contract;
|
|
14230
|
+
// if (!mergedCTR.chargingStationOperators)
|
|
14231
|
+
// mergedCTR.chargingStationOperators = ctr.chargingStationOperators;
|
|
14232
|
+
// else if (ctr.chargingStationOperators)
|
|
14233
|
+
// for (const chargingStationOperator of ctr.chargingStationOperators)
|
|
14234
|
+
// mergedCTR.chargingStationOperators.push(chargingStationOperator);
|
|
14235
|
+
// if (!mergedCTR.chargingPools)
|
|
14236
|
+
// mergedCTR.chargingPools = ctr.chargingPools;
|
|
14237
|
+
// else if (ctr.chargingPools)
|
|
14238
|
+
// for (const chargingPool of ctr.chargingPools)
|
|
14239
|
+
// mergedCTR.chargingPools.push(chargingPool);
|
|
14240
|
+
// if (!mergedCTR.chargingStations)
|
|
14241
|
+
// mergedCTR.chargingStations = ctr.chargingStations;
|
|
14242
|
+
// else if (ctr.chargingStations)
|
|
14243
|
+
// for (const chargingStation of ctr.chargingStations)
|
|
14244
|
+
// mergedCTR.chargingStations.push(chargingStation);
|
|
14245
|
+
// // publicKeys
|
|
14246
|
+
// if (!mergedCTR.chargingSessions)
|
|
14247
|
+
// mergedCTR.chargingSessions = ctr.chargingSessions;
|
|
14248
|
+
// else if (ctr.chargingSessions)
|
|
14249
|
+
// for (const chargingSession of ctr.chargingSessions)
|
|
14250
|
+
// mergedCTR.chargingSessions.push(chargingSession);
|
|
14251
|
+
// if (!mergedCTR.eMobilityProviders)
|
|
14252
|
+
// mergedCTR.eMobilityProviders = ctr.eMobilityProviders;
|
|
14253
|
+
// else if (ctr.eMobilityProviders)
|
|
14254
|
+
// for (const eMobilityProvider of ctr.eMobilityProviders)
|
|
14255
|
+
// mergedCTR.eMobilityProviders.push(eMobilityProvider);
|
|
14256
|
+
// if (!mergedCTR.mediationServices)
|
|
14257
|
+
// mergedCTR.mediationServices = ctr.mediationServices;
|
|
14258
|
+
// else if (ctr.mediationServices)
|
|
14259
|
+
// for (const mediationService of ctr.mediationServices)
|
|
14260
|
+
// mergedCTR.mediationServices.push(mediationService);
|
|
14261
|
+
// }
|
|
14262
|
+
// return mergedCTR;
|
|
14263
|
+
// }
|
|
14264
|
+
// //#endregion
|
|
14213
14265
|
//#region (private) validateOCMFSignature(OCMFJSONDocument, PublicKey, PublicKeyEncoding?)
|
|
14214
14266
|
async validateOCMFSignature(OCMFJSONDocument, PublicKey, PublicKeyEncoding) {
|
|
14215
14267
|
try {
|
|
@@ -14268,7 +14320,7 @@ var OCMF = class {
|
|
|
14268
14320
|
this.key("pubKey").bitstr()
|
|
14269
14321
|
);
|
|
14270
14322
|
});
|
|
14271
|
-
if (PublicKeyEncoding) {
|
|
14323
|
+
if (PublicKeyEncoding != null && PublicKeyEncoding.length > 0) {
|
|
14272
14324
|
switch (PublicKeyEncoding.toLowerCase()) {
|
|
14273
14325
|
case "hex":
|
|
14274
14326
|
publicKeyBytes = Buffer.from(OCMFJSONDocument.publicKey, "hex");
|
|
@@ -14296,11 +14348,12 @@ var OCMF = class {
|
|
|
14296
14348
|
publicKeyBytes = Buffer.from(OCMFJSONDocument.publicKey, "base64");
|
|
14297
14349
|
}
|
|
14298
14350
|
}
|
|
14299
|
-
if (
|
|
14351
|
+
if (publicKeyBytes == null) {
|
|
14300
14352
|
OCMFJSONDocument.validationStatus = "UnknownPublicKeyFormat" /* UnknownPublicKeyFormat */;
|
|
14301
14353
|
return OCMFJSONDocument.validationStatus;
|
|
14302
14354
|
}
|
|
14303
|
-
const
|
|
14355
|
+
const decodedPublicKeyASN1 = ECPoint.decode(publicKeyBytes, "der");
|
|
14356
|
+
const publicKeyASN1 = decodedPublicKeyASN1;
|
|
14304
14357
|
const coordinates = publicKeyASN1.pubKey.data.subarray(1);
|
|
14305
14358
|
const halfLength = coordinates.length / 2;
|
|
14306
14359
|
const publicKeyXY = {
|
|
@@ -14309,7 +14362,7 @@ var OCMF = class {
|
|
|
14309
14362
|
};
|
|
14310
14363
|
publicKey = curve.keyFromPublic(publicKeyXY, "hex");
|
|
14311
14364
|
OCMFJSONDocument.publicKey = {
|
|
14312
|
-
algorithm: OCMFJSONDocument.signature.SA ? OCMFJSONDocument.signature.SA.substring(
|
|
14365
|
+
algorithm: OCMFJSONDocument.signature.SA != null && OCMFJSONDocument.signature.SA.length > 0 ? OCMFJSONDocument.signature.SA.substring(
|
|
14313
14366
|
OCMFJSONDocument.signature.SA.indexOf("-") + 1,
|
|
14314
14367
|
OCMFJSONDocument.signature.SA.lastIndexOf("-")
|
|
14315
14368
|
) : "secp256r1",
|
|
@@ -14460,7 +14513,7 @@ var OCMF = class {
|
|
|
14460
14513
|
hashValue,
|
|
14461
14514
|
publicKey: PublicKey,
|
|
14462
14515
|
publicKeyEncoding: PublicKeyEncoding,
|
|
14463
|
-
validationStatus: validationStatus
|
|
14516
|
+
validationStatus: validationStatus ?? (PublicKey != null ? "Unvalidated" /* Unvalidated */ : "PublicKeyNotFound" /* PublicKeyNotFound */)
|
|
14464
14517
|
};
|
|
14465
14518
|
try {
|
|
14466
14519
|
const ECDSASignature = this.chargy.asn1.define("ECDSASignature", function() {
|
|
@@ -14482,7 +14535,8 @@ var OCMF = class {
|
|
|
14482
14535
|
default:
|
|
14483
14536
|
ocmfJSONDocument.validationStatus = "UnknownSignatureFormat" /* UnknownSignatureFormat */;
|
|
14484
14537
|
}
|
|
14485
|
-
const
|
|
14538
|
+
const decodedSignatureObj = ECDSASignature.decode(Buffer.from(ocmfJSONDocument.signature.SD, bufferEncoding), "der");
|
|
14539
|
+
const signatureObj = decodedSignatureObj;
|
|
14486
14540
|
ocmfJSONDocument.signatureRS = {
|
|
14487
14541
|
value: ocmfJSONDocument.signature.SD,
|
|
14488
14542
|
r: signatureObj.r.toString(16),
|
|
@@ -14491,7 +14545,7 @@ var OCMF = class {
|
|
|
14491
14545
|
} catch {
|
|
14492
14546
|
ocmfJSONDocument.validationStatus = "InvalidSignature" /* InvalidSignature */;
|
|
14493
14547
|
}
|
|
14494
|
-
if (PublicKey && ocmfJSONDocument.validationStatus === "Unvalidated" /* Unvalidated */)
|
|
14548
|
+
if (PublicKey != null && ocmfJSONDocument.validationStatus === "Unvalidated" /* Unvalidated */)
|
|
14495
14549
|
await this.validateOCMFSignature(
|
|
14496
14550
|
ocmfJSONDocument,
|
|
14497
14551
|
PublicKey,
|
|
@@ -14520,7 +14574,7 @@ var OCMF = class {
|
|
|
14520
14574
|
//#endregion
|
|
14521
14575
|
//#region TryToParseOCMFDocument (OCMFDocument, PublicKey?, PublicKeyEncoding?, ContainerInfos?)
|
|
14522
14576
|
async TryToParseOCMFDocument(OCMFDocument, PublicKey, PublicKeyEncoding, ContainerInfos) {
|
|
14523
|
-
return
|
|
14577
|
+
return this.TryToParseOCMFDocuments([OCMFDocument], PublicKey, PublicKeyEncoding, ContainerInfos);
|
|
14524
14578
|
}
|
|
14525
14579
|
//#endregion
|
|
14526
14580
|
//#region TryToParseOCMFDocuments(OCMFDocuments, PublicKey?, PublicKeyEncoding?, ContainerInfos?)
|
|
@@ -14540,7 +14594,8 @@ var OCMF = class {
|
|
|
14540
14594
|
if (typeof ocmfJSONDocuments !== "string") {
|
|
14541
14595
|
for (const ocmfJSONDocument of ocmfJSONDocuments) {
|
|
14542
14596
|
try {
|
|
14543
|
-
const groupingKey = (ocmfJSONDocument.payload.FV ?? "") + "|" + (ocmfJSONDocument.payload.GI ?? "") + "|" + (ocmfJSONDocument.payload.GS ?? "") + "|" + (ocmfJSONDocument.payload.GV ?? "") + "|" + (ocmfJSONDocument.payload.MV ?? "") + "|" + (ocmfJSONDocument.payload.MM ?? "") + "|" + (ocmfJSONDocument.payload.MS ?? "") + "|" + (ocmfJSONDocument.payload.MF ?? "") + "|" + (ocmfJSONDocument.payload.IS ? "1|" : "0|") + (ocmfJSONDocument.payload.IL ?? "") + "|" + (ocmfJSONDocument.payload.IF ?? "") + "|" +
|
|
14597
|
+
const groupingKey = (ocmfJSONDocument.payload.FV ?? "") + "|" + (ocmfJSONDocument.payload.GI ?? "") + "|" + (ocmfJSONDocument.payload.GS ?? "") + "|" + (ocmfJSONDocument.payload.GV ?? "") + "|" + (ocmfJSONDocument.payload.MV ?? "") + "|" + (ocmfJSONDocument.payload.MM ?? "") + "|" + (ocmfJSONDocument.payload.MS ?? "") + "|" + (ocmfJSONDocument.payload.MF ?? "") + "|" + (ocmfJSONDocument.payload.IS === true ? "1|" : "0|") + (ocmfJSONDocument.payload.IL ?? "") + "|" + //(ocmfJSONDocument.payload.IF ?? "") + "|" +
|
|
14598
|
+
(ocmfJSONDocument.payload.IT ?? "") + "|" + (ocmfJSONDocument.payload.ID ?? "") + "|" + (ocmfJSONDocument.payload.TT ?? "") + "|" + (ocmfJSONDocument.payload.CF ?? "") + "|" + (ocmfJSONDocument.payload.CT ?? "") + "|" + (ocmfJSONDocument.payload.CI ?? "");
|
|
14544
14599
|
if (!ocmfJSONDocumentGroups.has(groupingKey))
|
|
14545
14600
|
ocmfJSONDocumentGroups.set(groupingKey, new Array());
|
|
14546
14601
|
ocmfJSONDocumentGroups.get(groupingKey)?.push(ocmfJSONDocument);
|
|
@@ -14553,7 +14608,7 @@ var OCMF = class {
|
|
|
14553
14608
|
}
|
|
14554
14609
|
}
|
|
14555
14610
|
for (const ocmfJSONDocumentGroup of ocmfJSONDocumentGroups.values()) {
|
|
14556
|
-
if (PublicKey) {
|
|
14611
|
+
if (PublicKey != null) {
|
|
14557
14612
|
for (const ocmfJSONDocument of ocmfJSONDocumentGroup)
|
|
14558
14613
|
if (ocmfJSONDocument.validationStatus === "Unvalidated" /* Unvalidated */)
|
|
14559
14614
|
await this.validateOCMFSignature(ocmfJSONDocument, PublicKey);
|
|
@@ -14605,12 +14660,6 @@ var OCPI = class {
|
|
|
14605
14660
|
constructor(chargy) {
|
|
14606
14661
|
this.chargy = chargy;
|
|
14607
14662
|
}
|
|
14608
|
-
bufferToHex(buffer, Reverse) {
|
|
14609
|
-
return (Reverse ? Array.from(new Uint8Array(buffer)).reverse() : Array.from(new Uint8Array(buffer))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
14610
|
-
}
|
|
14611
|
-
bufferToNumber(buffer) {
|
|
14612
|
-
return parseInt(Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join(""), 16);
|
|
14613
|
-
}
|
|
14614
14663
|
//#region tryToParseChargeITContainerFormat(SomeJSON)
|
|
14615
14664
|
// The chargeIT mobility data format does not always provide context information or format identifiers!
|
|
14616
14665
|
async tryToParseOCPIFormat(SomeJSON) {
|
|
@@ -14644,7 +14693,7 @@ var OCPI = class {
|
|
|
14644
14693
|
};
|
|
14645
14694
|
if (encoding_method === "OCMF" && isMandatoryJSONArray(signed_values) && signed_values.length > 0) {
|
|
14646
14695
|
const firstSignedData = asString(asJSONObject(signed_values[0])?.["signed_data"]);
|
|
14647
|
-
if (firstSignedData) {
|
|
14696
|
+
if (firstSignedData != null && firstSignedData.length > 0) {
|
|
14648
14697
|
const placeInfo = asJSONObject(SomeJSON["placeInfo"]);
|
|
14649
14698
|
const geoLocation = asJSONObject(placeInfo?.["geoLocation"]);
|
|
14650
14699
|
const address = asJSONObject(placeInfo?.["address"]);
|
|
@@ -14663,7 +14712,7 @@ var OCPI = class {
|
|
|
14663
14712
|
},
|
|
14664
14713
|
energyMeter: asJSONObject(SomeJSON["meterInfo"])
|
|
14665
14714
|
};
|
|
14666
|
-
return
|
|
14715
|
+
return new OCMF(this.chargy).TryToParseOCMFDocument(
|
|
14667
14716
|
firstSignedData,
|
|
14668
14717
|
asString(public_key),
|
|
14669
14718
|
encoding_method,
|
|
@@ -14981,9 +15030,9 @@ var OCPI = class {
|
|
|
14981
15030
|
],
|
|
14982
15031
|
certainty: 0
|
|
14983
15032
|
};
|
|
14984
|
-
if (smvContext?.startsWith("https://www.chargeit-mobility.com/contexts/bsm-ws36a-json"))
|
|
15033
|
+
if (smvContext?.startsWith("https://www.chargeit-mobility.com/contexts/bsm-ws36a-json") === true)
|
|
14985
15034
|
return new BSMCrypt01(this.chargy).tryToParseBSM_WS36aMeasurements(CTR, evseIdStr, asString(chargingStation_controllerSoftwareVersion) ?? null, signedMeterValues);
|
|
14986
|
-
if (smvContext?.startsWith("ALFEN"))
|
|
15035
|
+
if (smvContext?.startsWith("ALFEN") === true)
|
|
14987
15036
|
return new Alfen(this.chargy).TryToParseALFENFormat(
|
|
14988
15037
|
signedMeterValues.map((value) => asString(asJSONObject(value)?.["payload"]) ?? ""),
|
|
14989
15038
|
{
|
|
@@ -15130,9 +15179,9 @@ function parsePCDFDocument(input) {
|
|
|
15130
15179
|
}
|
|
15131
15180
|
const fields = {};
|
|
15132
15181
|
for (let i = 0; i < PCDF_FIELD_ORDER.length; i++) {
|
|
15133
|
-
const value = match
|
|
15134
|
-
const field = PCDF_FIELD_ORDER
|
|
15135
|
-
if (
|
|
15182
|
+
const value = getArrayElement(match, i + 1, "Missing PCDF field value");
|
|
15183
|
+
const field = getArrayElement(PCDF_FIELD_ORDER, i, "Missing PCDF field name");
|
|
15184
|
+
if (value.length === 0)
|
|
15136
15185
|
throw new PCDFParseError("MISSING_FIELDS", "Missing fields in the data tuple", field);
|
|
15137
15186
|
fields[field] = value;
|
|
15138
15187
|
}
|
|
@@ -15504,8 +15553,10 @@ function parsePCDFReadingValue(value, errors) {
|
|
|
15504
15553
|
errors.push("Session information is invalid");
|
|
15505
15554
|
return void 0;
|
|
15506
15555
|
}
|
|
15556
|
+
const integralPart = getArrayElement(match, 1, "Missing PCDF reading integral part");
|
|
15557
|
+
const fractionalPart = getArrayElement(match, 2, "Missing PCDF reading fractional part");
|
|
15507
15558
|
return {
|
|
15508
|
-
value: new Decimal(
|
|
15559
|
+
value: new Decimal(integralPart + "." + fractionalPart),
|
|
15509
15560
|
unit: "kWh"
|
|
15510
15561
|
};
|
|
15511
15562
|
}
|
|
@@ -15540,9 +15591,9 @@ function hexToBytes2(hex, errorMessage) {
|
|
|
15540
15591
|
}
|
|
15541
15592
|
function readDERLength(bytes, getOffset, setOffset) {
|
|
15542
15593
|
let offset = getOffset();
|
|
15543
|
-
|
|
15544
|
-
if (b == null)
|
|
15594
|
+
if (offset >= bytes.length)
|
|
15545
15595
|
throw new Error("Invalid signature");
|
|
15596
|
+
const b = getArrayElement(bytes, offset++, "Invalid signature");
|
|
15546
15597
|
if ((b & 128) === 0) {
|
|
15547
15598
|
setOffset(offset);
|
|
15548
15599
|
return b;
|
|
@@ -15552,9 +15603,9 @@ function readDERLength(bytes, getOffset, setOffset) {
|
|
|
15552
15603
|
throw new Error("Invalid signature");
|
|
15553
15604
|
let length = 0;
|
|
15554
15605
|
for (let i = 0; i < octets; i++) {
|
|
15555
|
-
|
|
15556
|
-
if (value == null)
|
|
15606
|
+
if (offset >= bytes.length)
|
|
15557
15607
|
throw new Error("Invalid signature");
|
|
15608
|
+
const value = getArrayElement(bytes, offset++, "Invalid signature");
|
|
15558
15609
|
length = (length << 8) + value;
|
|
15559
15610
|
}
|
|
15560
15611
|
setOffset(offset);
|
|
@@ -15583,30 +15634,33 @@ var SAFEXML = class _SAFEXML {
|
|
|
15583
15634
|
this.chargy = chargy;
|
|
15584
15635
|
}
|
|
15585
15636
|
static ParseChargingStationContext(XMLDocument) {
|
|
15586
|
-
const chargingStationElement = getElementsByLocalName(XMLDocument, "chargingStation")
|
|
15587
|
-
if (
|
|
15637
|
+
const chargingStationElement = getElementsByLocalName(XMLDocument, "chargingStation").at(0);
|
|
15638
|
+
if (chargingStationElement === void 0)
|
|
15588
15639
|
return {};
|
|
15589
|
-
const
|
|
15640
|
+
const chargingStationAttributeId = chargingStationElement.getAttribute("id")?.trim();
|
|
15641
|
+
const chargingStationId = chargingStationAttributeId != null && chargingStationAttributeId.length > 0 ? chargingStationAttributeId : getTrimmedTextContent(getDirectChildByLocalName(chargingStationElement, "id"));
|
|
15590
15642
|
const softwareVersion = getTrimmedTextContent(getDirectChildByLocalName(chargingStationElement, "softwareVersion"));
|
|
15591
15643
|
const geoLocationElement = getDirectChildByLocalName(chargingStationElement, "geoLocation");
|
|
15592
15644
|
const latitude = Number.parseFloat(getTrimmedTextContent(getDirectChildByLocalName(geoLocationElement ?? chargingStationElement, "latitude")) ?? "");
|
|
15593
15645
|
const longitude = Number.parseFloat(getTrimmedTextContent(getDirectChildByLocalName(geoLocationElement ?? chargingStationElement, "longitude")) ?? "");
|
|
15594
15646
|
const geoLocation = Number.isFinite(latitude) && Number.isFinite(longitude) ? { lat: latitude, lng: longitude } : void 0;
|
|
15595
|
-
if (getDirectChildByLocalName(chargingStationElement, "EVSEs"))
|
|
15647
|
+
if (getDirectChildByLocalName(chargingStationElement, "EVSEs") !== void 0)
|
|
15596
15648
|
throw new Error("The SAFE chargingStation XML element must contain EVSE directly and no EVSEs container element!");
|
|
15597
15649
|
const evseElements = getDirectChildrenByLocalName(chargingStationElement, "EVSE");
|
|
15598
15650
|
if (evseElements.length > 1)
|
|
15599
15651
|
throw new Error("The SAFE chargingStation XML element must not contain more than one EVSE element!");
|
|
15600
|
-
const evseElement = evseElements
|
|
15601
|
-
const
|
|
15602
|
-
const
|
|
15652
|
+
const evseElement = evseElements.at(0);
|
|
15653
|
+
const evseAttributeId = evseElement?.getAttribute("id")?.trim();
|
|
15654
|
+
const evseTextId = getTrimmedTextContent(getDirectChildByLocalName(evseElement ?? chargingStationElement, "id"));
|
|
15655
|
+
const evseId = evseAttributeId != null && evseAttributeId.length > 0 ? evseAttributeId : evseTextId ?? "";
|
|
15656
|
+
const connectorElements = evseElement !== void 0 ? getDirectChildrenByLocalName(evseElement, "connector") : [];
|
|
15603
15657
|
if (connectorElements.length > 1)
|
|
15604
15658
|
throw new Error("The SAFE EVSE XML element must not contain more than one connector element!");
|
|
15605
|
-
const connectorElement = connectorElements
|
|
15659
|
+
const connectorElement = connectorElements.at(0);
|
|
15606
15660
|
const connectorId = connectorElement?.getAttribute("id")?.trim();
|
|
15607
15661
|
const connectorType = getTrimmedTextContent(getDirectChildByLocalName(connectorElement ?? evseElement ?? chargingStationElement, "type"));
|
|
15608
|
-
const connector = connectorType ? { "@id": connectorId, type: connectorType, looses: 0 } : void 0;
|
|
15609
|
-
const parsedEVSE = evseElement ? {
|
|
15662
|
+
const connector = connectorType != null && connectorType.length > 0 ? { "@id": connectorId, type: connectorType, looses: 0 } : void 0;
|
|
15663
|
+
const parsedEVSE = evseElement !== void 0 ? {
|
|
15610
15664
|
"@id": evseId,
|
|
15611
15665
|
"description": parseDescription(evseElement),
|
|
15612
15666
|
"meters": [],
|
|
@@ -15771,8 +15825,8 @@ var XMLContainer = class {
|
|
|
15771
15825
|
if (values.length == 1) {
|
|
15772
15826
|
const valueList = values[0]?.querySelectorAll("signedMeterValue");
|
|
15773
15827
|
if (valueList != null && valueList.length >= 1) {
|
|
15774
|
-
for (
|
|
15775
|
-
const publicKey =
|
|
15828
|
+
for (const valueElement of valueList) {
|
|
15829
|
+
const publicKey = valueElement.querySelector("publicKey");
|
|
15776
15830
|
if (publicKey != null) {
|
|
15777
15831
|
const publicKeyEncoding = publicKey.attributes.getNamedItem("encoding")?.value.trim().toLowerCase() ?? "";
|
|
15778
15832
|
let publicKeyValue = publicKey.textContent.trim();
|
|
@@ -15812,7 +15866,7 @@ var XMLContainer = class {
|
|
|
15812
15866
|
certainty: 0
|
|
15813
15867
|
};
|
|
15814
15868
|
}
|
|
15815
|
-
const meterValueSignature =
|
|
15869
|
+
const meterValueSignature = valueElement.querySelector("meterValueSignature");
|
|
15816
15870
|
if (meterValueSignature != null) {
|
|
15817
15871
|
const meterValueSignatureEncoding = meterValueSignature.attributes.getNamedItem("encoding")?.value.trim().toLowerCase() ?? "";
|
|
15818
15872
|
let meterValueSignatureValue = meterValueSignature.textContent.trim();
|
|
@@ -15845,7 +15899,7 @@ var XMLContainer = class {
|
|
|
15845
15899
|
};
|
|
15846
15900
|
common.meterValueSignatures.push(meterValueSignatureValue);
|
|
15847
15901
|
}
|
|
15848
|
-
const signatureMethod =
|
|
15902
|
+
const signatureMethod = valueElement.querySelector("signatureMethod")?.textContent.trim().toLowerCase() ?? "";
|
|
15849
15903
|
if (common.signatureMethod == "")
|
|
15850
15904
|
common.signatureMethod = signatureMethod;
|
|
15851
15905
|
else if (common.signatureMethod != signatureMethod)
|
|
@@ -15854,7 +15908,7 @@ var XMLContainer = class {
|
|
|
15854
15908
|
message: this.chargy.GetMultilanguageText("Invalid mixture of different signature methods within the given XML container!"),
|
|
15855
15909
|
certainty: 0
|
|
15856
15910
|
};
|
|
15857
|
-
const encodingMethod =
|
|
15911
|
+
const encodingMethod = valueElement.querySelector("encodingMethod")?.textContent.trim().toLowerCase() ?? "";
|
|
15858
15912
|
if (common.encodingMethod == "")
|
|
15859
15913
|
common.encodingMethod = encodingMethod;
|
|
15860
15914
|
else if (common.encodingMethod != encodingMethod)
|
|
@@ -15863,7 +15917,7 @@ var XMLContainer = class {
|
|
|
15863
15917
|
message: this.chargy.GetMultilanguageText("Invalid mixture of different signed data formats within the given XML container!"),
|
|
15864
15918
|
certainty: 0
|
|
15865
15919
|
};
|
|
15866
|
-
const encodedMeterValue =
|
|
15920
|
+
const encodedMeterValue = valueElement.querySelector("encodedMeterValue");
|
|
15867
15921
|
if (encodedMeterValue != null) {
|
|
15868
15922
|
const signedDataEncoding = encodedMeterValue.attributes.getNamedItem("encoding")?.value.trim() ?? "";
|
|
15869
15923
|
let signedDataValue = encodedMeterValue.textContent.trim();
|
|
@@ -15924,12 +15978,6 @@ var ChargeIT = class {
|
|
|
15924
15978
|
constructor(chargy) {
|
|
15925
15979
|
this.chargy = chargy;
|
|
15926
15980
|
}
|
|
15927
|
-
bufferToHex(buffer, Reverse) {
|
|
15928
|
-
return (Reverse ? Array.from(new Uint8Array(buffer)).reverse() : Array.from(new Uint8Array(buffer))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
15929
|
-
}
|
|
15930
|
-
bufferToNumber(buffer) {
|
|
15931
|
-
return parseInt(Array.from(new Uint8Array(buffer)).map((b) => b.toString(16).padStart(2, "0")).join(""), 16);
|
|
15932
|
-
}
|
|
15933
15981
|
//#region TryToParseOldChargeITMeterValuesFormat(CTR, evseId, address, geoLocation, signedMeterValues)
|
|
15934
15982
|
TryToParseOldChargeITMeterValuesFormat(CTR, evseId, address, geoLocation, signedMeterValues) {
|
|
15935
15983
|
const CTRArray = new Array();
|
|
@@ -16029,14 +16077,8 @@ var ChargeIT = class {
|
|
|
16029
16077
|
});
|
|
16030
16078
|
}
|
|
16031
16079
|
}
|
|
16032
|
-
const firstMeterValue = CTRArray
|
|
16033
|
-
const lastMeterValue = CTRArray
|
|
16034
|
-
if (firstMeterValue === void 0 || lastMeterValue === void 0)
|
|
16035
|
-
return {
|
|
16036
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
16037
|
-
message: this.chargy.GetMultilanguageText("UnknownOrInvalidChargingSessionFormat"),
|
|
16038
|
-
certainty: 0
|
|
16039
|
-
};
|
|
16080
|
+
const firstMeterValue = getFirstArrayElement(CTRArray, "Missing first chargeIT meter value");
|
|
16081
|
+
const lastMeterValue = getLastArrayElement(CTRArray, "Missing last chargeIT meter value");
|
|
16040
16082
|
CTR["@id"] = lastMeterValue["transactionId"];
|
|
16041
16083
|
CTR.begin = this.chargy.moment.unix(firstMeterValue["measuredValue"]["timestampLocal"]["timestamp"]).utc().format();
|
|
16042
16084
|
CTR.end = this.chargy.moment.unix(lastMeterValue["measuredValue"]["timestampLocal"]["timestamp"]).utc().format();
|
|
@@ -16266,7 +16308,6 @@ var ChargeIT = class {
|
|
|
16266
16308
|
if (consistent) {
|
|
16267
16309
|
if (signedMeterValueContext === "https://www.lichtblick.de/contexts/bsm-ws36a-json-v0" || signedMeterValueContext === "https://www.lichtblick.de/contexts/bsm-ws36a-json-v1" || signedMeterValueContext === "https://www.eneco.com/contexts/bsm-ws36a-json-v0" || signedMeterValueContext === "https://www.eneco.com/contexts/bsm-ws36a-json-v1" || signedMeterValueContext === "https://www.chargeit-mobility.com/contexts/bsm-ws36a-json-v0" || signedMeterValueContext === "https://www.chargeit-mobility.com/contexts/bsm-ws36a-json-v1") {
|
|
16268
16310
|
} else if (signedMeterValueContext.startsWith("ALFEN")) {
|
|
16269
|
-
} else if (containerFormat === oldChargeITContainerFormat) {
|
|
16270
16311
|
} else if (signedMeterValueContext === oldChargeITMeterValueFormat) {
|
|
16271
16312
|
let measurementCounter = 0;
|
|
16272
16313
|
for (const signedMeterValue of signedMeterValues) {
|
|
@@ -16382,10 +16423,10 @@ var ChargeIT = class {
|
|
|
16382
16423
|
}
|
|
16383
16424
|
}
|
|
16384
16425
|
const chargePoint = signedMeterValue["chargePoint"];
|
|
16385
|
-
if (!isMandatoryJSONObject(chargePoint)) {
|
|
16426
|
+
if (chargePoint !== void 0 && !isMandatoryJSONObject(chargePoint)) {
|
|
16386
16427
|
errors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("MissingOrInvalid_SignedMeterValue_ChargePointInformationP", measurementCounter)));
|
|
16387
16428
|
secondaryErrors += 1;
|
|
16388
|
-
} else {
|
|
16429
|
+
} else if (isMandatoryJSONObject(chargePoint)) {
|
|
16389
16430
|
if (!isMandatoryString(chargePoint["softwareVersion"]))
|
|
16390
16431
|
errors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("MissingOrInvalid_SignedMeterValue_ChargePointInformation_SoftwareVersionP", measurementCounter)));
|
|
16391
16432
|
}
|
|
@@ -16533,19 +16574,17 @@ var ChargeIT = class {
|
|
|
16533
16574
|
}
|
|
16534
16575
|
);
|
|
16535
16576
|
}
|
|
16536
|
-
if (
|
|
16537
|
-
|
|
16538
|
-
|
|
16539
|
-
|
|
16540
|
-
|
|
16541
|
-
|
|
16542
|
-
|
|
16543
|
-
|
|
16544
|
-
address: { "street": address_street, "postalCode": address_zipCode, "city": address_town, "country": address_country }
|
|
16545
|
-
}
|
|
16577
|
+
if (asJSONObject(signedMeterValues[0])?.["format"] == "ALFEN")
|
|
16578
|
+
return new Alfen(this.chargy).TryToParseALFENFormat(
|
|
16579
|
+
signedMeterValues.map((value) => asString(asJSONObject(value)?.["payload"]) ?? ""),
|
|
16580
|
+
{
|
|
16581
|
+
EVSEId: evseId,
|
|
16582
|
+
chargingStation: {
|
|
16583
|
+
geoLocation: { "lat": geoLocation_lat, "lng": geoLocation_lon },
|
|
16584
|
+
address: { "street": address_street, "postalCode": address_zipCode, "city": address_town, "country": address_country }
|
|
16546
16585
|
}
|
|
16547
|
-
|
|
16548
|
-
|
|
16586
|
+
}
|
|
16587
|
+
);
|
|
16549
16588
|
if (signedMeterValueContext === oldChargeITMeterValueFormat)
|
|
16550
16589
|
return this.TryToParseOldChargeITMeterValuesFormat(
|
|
16551
16590
|
CTR,
|
|
@@ -16735,8 +16774,6 @@ var ChargeIT = class {
|
|
|
16735
16774
|
errors.push(CreateError(this.chargy.GetMultilanguageTextWithParameter("MissingOrInvalid_SignedMeterValue_MeterInfo_MeterIdP", 1)));
|
|
16736
16775
|
}
|
|
16737
16776
|
if (isMandatoryJSONArray(chargingTariffs)) {
|
|
16738
|
-
for (let i = 0; i < chargingTariffs.length; i++) {
|
|
16739
|
-
}
|
|
16740
16777
|
}
|
|
16741
16778
|
if (totalCosts) {
|
|
16742
16779
|
if (!isMandatoryNumber(totalCosts_total))
|
|
@@ -16889,9 +16926,9 @@ var ChargeIT = class {
|
|
|
16889
16926
|
],
|
|
16890
16927
|
certainty: 0
|
|
16891
16928
|
};
|
|
16892
|
-
if (smvContext?.startsWith("https://www.chargeit-mobility.com/contexts/bsm-ws36a-json"))
|
|
16929
|
+
if (smvContext?.startsWith("https://www.chargeit-mobility.com/contexts/bsm-ws36a-json") === true)
|
|
16893
16930
|
return new BSMCrypt01(this.chargy).tryToParseBSM_WS36aMeasurements(CTR, evseIdStr, asString(chargingStation_controllerSoftwareVersion) ?? null, signedMeterValues);
|
|
16894
|
-
if (smvContext?.startsWith("ALFEN"))
|
|
16931
|
+
if (smvContext?.startsWith("ALFEN") === true)
|
|
16895
16932
|
return new Alfen(this.chargy).TryToParseALFENFormat(
|
|
16896
16933
|
signedMeterValues.map((value) => asString(asJSONObject(value)?.["payload"]) ?? ""),
|
|
16897
16934
|
{
|
|
@@ -16994,10 +17031,8 @@ var ChargePoint = class {
|
|
|
16994
17031
|
if (startTimeUTC !== void 0 && (parkingEnd === void 0 || startTimeUTC > parkingEnd))
|
|
16995
17032
|
parkingEnd = endTimeUTC;
|
|
16996
17033
|
}
|
|
16997
|
-
|
|
16998
|
-
|
|
16999
|
-
if (chargingEnd === void 0)
|
|
17000
|
-
chargingEnd = parkingEnd;
|
|
17034
|
+
chargingStart ??= parkingStart;
|
|
17035
|
+
chargingEnd ??= parkingEnd;
|
|
17001
17036
|
let sessionStart = parkingStart !== void 0 && (chargingStart === void 0 || parkingStart < chargingStart) ? parkingStart : chargingStart;
|
|
17002
17037
|
if (sessionStart === void 0 && parkingStart !== void 0)
|
|
17003
17038
|
sessionStart = parkingStart;
|
|
@@ -17146,7 +17181,7 @@ var ChargePoint = class {
|
|
|
17146
17181
|
"status": "Unvalidated" /* Unvalidated */
|
|
17147
17182
|
};
|
|
17148
17183
|
if (parking && parking.length > 0 && CTR.chargingSessions && CTR.chargingSessions.length > 0) {
|
|
17149
|
-
const chargingSession = CTR.chargingSessions
|
|
17184
|
+
const chargingSession = getFirstArrayElement(CTR.chargingSessions, "Missing ChargePoint charging session");
|
|
17150
17185
|
chargingSession.parking = [];
|
|
17151
17186
|
for (const parkingItem of parking) {
|
|
17152
17187
|
const parkingObj = asJSONObject(parkingItem);
|
|
@@ -17442,6 +17477,8 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17442
17477
|
case "InvalidSignature" /* InvalidSignature */:
|
|
17443
17478
|
currentResult.status = "InvalidStartValue" /* InvalidStartValue */;
|
|
17444
17479
|
break;
|
|
17480
|
+
default:
|
|
17481
|
+
break;
|
|
17445
17482
|
}
|
|
17446
17483
|
} else if (i == measurement.values.length - 1) {
|
|
17447
17484
|
switch (currentResult.status) {
|
|
@@ -17454,6 +17491,8 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17454
17491
|
case "InvalidSignature" /* InvalidSignature */:
|
|
17455
17492
|
currentResult.status = "InvalidStopValue" /* InvalidStopValue */;
|
|
17456
17493
|
break;
|
|
17494
|
+
default:
|
|
17495
|
+
break;
|
|
17457
17496
|
}
|
|
17458
17497
|
} else {
|
|
17459
17498
|
switch (currentResult.status) {
|
|
@@ -17466,6 +17505,8 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17466
17505
|
case "InvalidSignature" /* InvalidSignature */:
|
|
17467
17506
|
currentResult.status = "InvalidStopValue" /* InvalidStopValue */;
|
|
17468
17507
|
break;
|
|
17508
|
+
default:
|
|
17509
|
+
break;
|
|
17469
17510
|
}
|
|
17470
17511
|
}
|
|
17471
17512
|
}
|
|
@@ -17495,9 +17536,9 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17495
17536
|
const cryptoResult = {
|
|
17496
17537
|
status: "NoOperation" /* NoOperation */
|
|
17497
17538
|
};
|
|
17498
|
-
return
|
|
17539
|
+
return Promise.resolve(setResult("NoOperation" /* NoOperation */));
|
|
17499
17540
|
}
|
|
17500
|
-
async ViewMeasurement(measurementValue,
|
|
17541
|
+
async ViewMeasurement(measurementValue, _errorDiv, introDiv, _infoDiv, PlainTextDiv, HashedPlainTextDiv, PublicKeyDiv, SignatureExpectedDiv, SignatureCheckDiv) {
|
|
17501
17542
|
if (measurementValue.measurement?.chargingSession === void 0)
|
|
17502
17543
|
return new Error("Invalid measurement!");
|
|
17503
17544
|
const chargingSession = measurementValue.measurement.chargingSession;
|
|
@@ -17505,8 +17546,8 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17505
17546
|
const algorithmName = typeof chargingSession.publicKey?.algorithm === "object" ? chargingSession.publicKey.algorithm.name : chargingSession.publicKey?.algorithm ?? "";
|
|
17506
17547
|
const algorithmType = typeof chargingSession.publicKey?.type === "object" ? chargingSession.publicKey.type.name : chargingSession.publicKey?.type;
|
|
17507
17548
|
introDiv.innerHTML = this.chargy.GetLocalizedMessage("The following data of the charging session is relevant for metrological and legal metrological purposes and therefore part of the digital signature").replace("{methodName}", "ChargePointCrypt01").replace("{cryptoAlgorithm}", algorithmName);
|
|
17508
|
-
if (PlainTextDiv.parentElement
|
|
17509
|
-
PlainTextDiv.parentElement.children
|
|
17549
|
+
if (PlainTextDiv.parentElement) {
|
|
17550
|
+
getArrayLikeElement(PlainTextDiv.parentElement.children, 0, "Missing plain text header").innerHTML = "Plain text (secrrct)";
|
|
17510
17551
|
}
|
|
17511
17552
|
PlainTextDiv.innerText = atob(chargingSession.original ?? "");
|
|
17512
17553
|
PlainTextDiv.style.fontFamily = "monospace";
|
|
@@ -17528,22 +17569,23 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17528
17569
|
hashInfo = "(SHA512, 512 Bits, hex)";
|
|
17529
17570
|
break;
|
|
17530
17571
|
}
|
|
17531
|
-
if (HashedPlainTextDiv.parentElement
|
|
17532
|
-
HashedPlainTextDiv.parentElement.children
|
|
17572
|
+
if (HashedPlainTextDiv.parentElement) {
|
|
17573
|
+
getArrayLikeElement(HashedPlainTextDiv.parentElement.children, 0, "Missing hashed plain text header").innerHTML = "Hashed plain text " + hashInfo;
|
|
17533
17574
|
}
|
|
17534
17575
|
HashedPlainTextDiv.innerHTML = chargingSession.hashValue?.match(/.{1,8}/g)?.join(" ") ?? "0x00000000000000000000000000000000000";
|
|
17535
17576
|
if (chargingSession.publicKey != null) {
|
|
17536
|
-
if (PublicKeyDiv.parentElement
|
|
17537
|
-
PublicKeyDiv.parentElement.children
|
|
17577
|
+
if (PublicKeyDiv.parentElement) {
|
|
17578
|
+
getArrayLikeElement(PublicKeyDiv.parentElement.children, 0, "Missing public key header").innerHTML = "Public Key (" + (algorithmType != null && algorithmType.length > 0 ? algorithmType + ", " : "") + (algorithmName.length > 0 ? algorithmName + ", " : "") + "hex)";
|
|
17538
17579
|
}
|
|
17539
17580
|
PublicKeyDiv.innerHTML = chargingSession.publicKey.value.startsWith("04") ? '<span class="leadingFour">04</span> ' + (chargingSession.publicKey.value.substring(2).match(/.{1,8}/g)?.join(" ") ?? "") : chargingSession.publicKey.value.match(/.{1,8}/g)?.join(" ") ?? "";
|
|
17540
|
-
|
|
17541
|
-
|
|
17581
|
+
const publicKeySignatureContainer = PublicKeyDiv.parentElement?.children[3];
|
|
17582
|
+
if (publicKeySignatureContainer !== void 0) {
|
|
17583
|
+
publicKeySignatureContainer.innerHTML = "";
|
|
17542
17584
|
}
|
|
17543
17585
|
if (result.publicKeySignatures) {
|
|
17544
17586
|
for (const signature of result.publicKeySignatures) {
|
|
17545
17587
|
try {
|
|
17546
|
-
const signatureDiv =
|
|
17588
|
+
const signatureDiv = publicKeySignatureContainer?.appendChild(document.createElement("div"));
|
|
17547
17589
|
if (signatureDiv != null)
|
|
17548
17590
|
signatureDiv.innerHTML = await this.chargy.CheckMeterPublicKeySignature(
|
|
17549
17591
|
measurementValue.measurement.chargingSession.chargingStation,
|
|
@@ -17558,8 +17600,8 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17558
17600
|
}
|
|
17559
17601
|
}
|
|
17560
17602
|
if (chargingSession.signature != null) {
|
|
17561
|
-
if (SignatureExpectedDiv.parentElement
|
|
17562
|
-
SignatureExpectedDiv.parentElement.children
|
|
17603
|
+
if (SignatureExpectedDiv.parentElement) {
|
|
17604
|
+
getArrayLikeElement(SignatureExpectedDiv.parentElement.children, 0, "Missing expected signature header").innerHTML = "Erwartete Signatur (secrrct.sign, rs, hex)";
|
|
17563
17605
|
}
|
|
17564
17606
|
if (typeof chargingSession.signature != "string")
|
|
17565
17607
|
SignatureExpectedDiv.innerHTML = "r: " + (chargingSession.signature.r?.toLowerCase().padStart(56, "0").match(/.{1,8}/g)?.join(" ") ?? "") + "<br />s: " + (chargingSession.signature.s?.toLowerCase().padStart(56, "0").match(/.{1,8}/g)?.join(" ") ?? "");
|
|
@@ -17591,6 +17633,7 @@ var ChargePointCrypt01 = class extends ACrypt {
|
|
|
17591
17633
|
break;
|
|
17592
17634
|
}
|
|
17593
17635
|
}
|
|
17636
|
+
return void 0;
|
|
17594
17637
|
}
|
|
17595
17638
|
};
|
|
17596
17639
|
|
|
@@ -17614,8 +17657,7 @@ function IsAChargeTransparencyRecord(data) {
|
|
|
17614
17657
|
function IsASessionCryptoResult(data) {
|
|
17615
17658
|
if (!isMandatoryJSONObject(data))
|
|
17616
17659
|
return false;
|
|
17617
|
-
|
|
17618
|
-
return sessionCryptoResult.status !== void 0;
|
|
17660
|
+
return true;
|
|
17619
17661
|
}
|
|
17620
17662
|
function CloneCTR(CTR) {
|
|
17621
17663
|
const clonedCTR = JSON.parse(JSON.stringify(CTR));
|
|
@@ -17794,8 +17836,10 @@ async function imageDataFromNodeCanvas(data) {
|
|
|
17794
17836
|
if (!isCanvasModule(canvasLib))
|
|
17795
17837
|
return void 0;
|
|
17796
17838
|
const image = await canvasLib.loadImage(import_buffer.Buffer.from(toUint8Array(data)));
|
|
17797
|
-
const
|
|
17798
|
-
const
|
|
17839
|
+
const naturalWidth = image.naturalWidth ?? 0;
|
|
17840
|
+
const naturalHeight = image.naturalHeight ?? 0;
|
|
17841
|
+
const width = naturalWidth > 0 ? naturalWidth : image.width;
|
|
17842
|
+
const height = naturalHeight > 0 ? naturalHeight : image.height;
|
|
17799
17843
|
if (width <= 0 || height <= 0)
|
|
17800
17844
|
return void 0;
|
|
17801
17845
|
const canvas = canvasLib.createCanvas(width, height);
|
|
@@ -17866,22 +17910,22 @@ async function importOptionalNodeModule(moduleName) {
|
|
|
17866
17910
|
}
|
|
17867
17911
|
async function readQRCodeTextFromImage(data, mimeType) {
|
|
17868
17912
|
const imageDataSources = typeof document === "undefined" ? [
|
|
17869
|
-
() => imageDataFromNodeCanvas(data),
|
|
17870
|
-
() => imageDataFromPNGOrJPEG(data, mimeType),
|
|
17871
|
-
() => imageDataFromBrowserCanvas(data, mimeType),
|
|
17872
|
-
() => imageDataFromBrowserImageElement(data, mimeType)
|
|
17913
|
+
async () => imageDataFromNodeCanvas(data),
|
|
17914
|
+
async () => imageDataFromPNGOrJPEG(data, mimeType),
|
|
17915
|
+
async () => imageDataFromBrowserCanvas(data, mimeType),
|
|
17916
|
+
async () => imageDataFromBrowserImageElement(data, mimeType)
|
|
17873
17917
|
] : [
|
|
17874
|
-
() => imageDataFromBrowserCanvas(data, mimeType),
|
|
17875
|
-
() => imageDataFromBrowserImageElement(data, mimeType),
|
|
17876
|
-
() => imageDataFromNodeCanvas(data),
|
|
17877
|
-
() => imageDataFromPNGOrJPEG(data, mimeType)
|
|
17918
|
+
async () => imageDataFromBrowserCanvas(data, mimeType),
|
|
17919
|
+
async () => imageDataFromBrowserImageElement(data, mimeType),
|
|
17920
|
+
async () => imageDataFromNodeCanvas(data),
|
|
17921
|
+
async () => imageDataFromPNGOrJPEG(data, mimeType)
|
|
17878
17922
|
];
|
|
17879
17923
|
for (const imageDataSource of imageDataSources) {
|
|
17880
17924
|
const imageData = await imageDataSource();
|
|
17881
17925
|
if (imageData == null)
|
|
17882
17926
|
continue;
|
|
17883
17927
|
const qrText = readQRCodeTextFromImageData(imageData);
|
|
17884
|
-
if (qrText && qrText.length > 0)
|
|
17928
|
+
if (qrText != null && qrText.length > 0)
|
|
17885
17929
|
return qrText;
|
|
17886
17930
|
}
|
|
17887
17931
|
return void 0;
|
|
@@ -17894,7 +17938,7 @@ function readQRCodeTextFromImageData(imageData) {
|
|
|
17894
17938
|
{ inversionAttempts: "attemptBoth" }
|
|
17895
17939
|
);
|
|
17896
17940
|
const qrText = qrCode?.data.trim();
|
|
17897
|
-
return qrText && qrText.length > 0 ? qrText : void 0;
|
|
17941
|
+
return qrText != null && qrText.length > 0 ? qrText : void 0;
|
|
17898
17942
|
}
|
|
17899
17943
|
|
|
17900
17944
|
// src/pdfjs/browser.ts
|
|
@@ -17915,6 +17959,11 @@ var validationRules_default = {
|
|
|
17915
17959
|
}
|
|
17916
17960
|
}
|
|
17917
17961
|
};
|
|
17962
|
+
function isPdfAttachment(value) {
|
|
17963
|
+
if (!isMandatoryJSONObject(value))
|
|
17964
|
+
return false;
|
|
17965
|
+
return typeof value["filename"] === "string" && (value["content"] instanceof ArrayBuffer || ArrayBuffer.isView(value["content"]));
|
|
17966
|
+
}
|
|
17918
17967
|
var Chargy = class {
|
|
17919
17968
|
//#region Data
|
|
17920
17969
|
i18n;
|
|
@@ -17931,8 +17980,6 @@ var Chargy = class {
|
|
|
17931
17980
|
EVSEs = new Array();
|
|
17932
17981
|
meters = new Array();
|
|
17933
17982
|
chargingSessions = new Array();
|
|
17934
|
-
eMobilityProviders = new Array();
|
|
17935
|
-
mediationServices = new Array();
|
|
17936
17983
|
currentCTR = {};
|
|
17937
17984
|
internalCTR = {};
|
|
17938
17985
|
//#endregion
|
|
@@ -17946,41 +17993,55 @@ var Chargy = class {
|
|
|
17946
17993
|
this.showPKIDetails = ShowPKIDetails;
|
|
17947
17994
|
this.validationRules = validationRules;
|
|
17948
17995
|
}
|
|
17949
|
-
|
|
17950
|
-
|
|
17996
|
+
fileNameWithoutExtension(fileName) {
|
|
17997
|
+
const lastSeparator = Math.max(fileName.lastIndexOf("/"), fileName.lastIndexOf("\\"));
|
|
17998
|
+
const lastDot = fileName.lastIndexOf(".");
|
|
17999
|
+
if (lastDot > lastSeparator)
|
|
18000
|
+
return fileName.substring(0, lastDot);
|
|
18001
|
+
return fileName;
|
|
17951
18002
|
}
|
|
17952
|
-
|
|
17953
|
-
|
|
17954
|
-
|
|
17955
|
-
|
|
17956
|
-
|
|
17957
|
-
|
|
18003
|
+
tryExtractChargeTransparencyTextFromURL(qrText) {
|
|
18004
|
+
if (!qrText.startsWith("http://") && !qrText.startsWith("https://"))
|
|
18005
|
+
return qrText;
|
|
18006
|
+
try {
|
|
18007
|
+
const url = new URL(qrText);
|
|
18008
|
+
const candidates = [
|
|
18009
|
+
url.hash.startsWith("#") ? url.hash.substring(1) : url.hash,
|
|
18010
|
+
url.searchParams.get("data"),
|
|
18011
|
+
url.searchParams.get("content"),
|
|
18012
|
+
url.searchParams.get("ctr"),
|
|
18013
|
+
url.searchParams.get("record"),
|
|
18014
|
+
url.searchParams.get("payload"),
|
|
18015
|
+
url.searchParams.get("q")
|
|
18016
|
+
];
|
|
18017
|
+
for (const candidate of candidates) {
|
|
18018
|
+
const decodedCandidate = candidate != null ? decodeURIComponent(candidate).trim() : "";
|
|
18019
|
+
if (decodedCandidate.startsWith("<?xml") || decodedCandidate.startsWith("<values") || decodedCandidate.startsWith("{") || decodedCandidate.startsWith("[") || decodedCandidate.startsWith("OCMF") || decodedCandidate.startsWith("AP;")) {
|
|
18020
|
+
return decodedCandidate;
|
|
18021
|
+
}
|
|
17958
18022
|
}
|
|
18023
|
+
} catch {
|
|
18024
|
+
console.log("Invalid URL in QR code: " + qrText);
|
|
17959
18025
|
}
|
|
17960
|
-
return
|
|
18026
|
+
return qrText;
|
|
17961
18027
|
}
|
|
17962
|
-
|
|
17963
|
-
|
|
17964
|
-
|
|
17965
|
-
|
|
17966
|
-
|
|
18028
|
+
tryExtractSignedDataTextFromXML(qrText) {
|
|
18029
|
+
const trimmedQRCodeText = qrText.trim();
|
|
18030
|
+
if (!trimmedQRCodeText.startsWith("<?xml") && !trimmedQRCodeText.startsWith("<"))
|
|
18031
|
+
return qrText;
|
|
18032
|
+
try {
|
|
18033
|
+
const xmlDocument = new DOMParser().parseFromString(trimmedQRCodeText, "text/xml");
|
|
18034
|
+
const signedDataValues = getElementsByLocalName(xmlDocument, "signedData").filter((signedData) => (signedData.getAttribute("format") ?? "").trim().toLowerCase() === "alfen").map((signedData) => signedData.textContent.trim()).filter((signedData) => signedData.startsWith("AP;"));
|
|
18035
|
+
if (signedDataValues.length > 0)
|
|
18036
|
+
return signedDataValues.join("\n");
|
|
18037
|
+
} catch {
|
|
18038
|
+
console.log("Error parsing XML content from QR code: " + qrText);
|
|
17967
18039
|
}
|
|
17968
|
-
|
|
17969
|
-
if (english !== void 0)
|
|
17970
|
-
return english;
|
|
17971
|
-
return Object.values(Text).find((value) => value !== void 0);
|
|
17972
|
-
}
|
|
17973
|
-
CompleteMultilanguageText(Text, fallbackText) {
|
|
17974
|
-
const result = {
|
|
17975
|
-
...Text ?? {}
|
|
17976
|
-
};
|
|
17977
|
-
const fallback = this.FindBestMultilanguageText(result) ?? fallbackText;
|
|
17978
|
-
for (const language of this.uiLanguages)
|
|
17979
|
-
result[language] ??= fallback;
|
|
17980
|
-
return result;
|
|
18040
|
+
return qrText;
|
|
17981
18041
|
}
|
|
18042
|
+
//#region Public key methods...
|
|
17982
18043
|
PublicKeyIdFromFileName(fileName) {
|
|
17983
|
-
return (fileName.
|
|
18044
|
+
return (fileName.includes(".") ? fileName.substring(0, fileName.indexOf(".")) : fileName).replace(/[-_]?public[-_]?key/i, "");
|
|
17984
18045
|
}
|
|
17985
18046
|
TryToParseDERPublicKey(keyId, publicKeyBuffer) {
|
|
17986
18047
|
const ASN1_OIDs = this.asn1.define("OIDs", function() {
|
|
@@ -18061,66 +18122,13 @@ var Chargy = class {
|
|
|
18061
18122
|
return textContent.replace(/\s+/g, "");
|
|
18062
18123
|
return void 0;
|
|
18063
18124
|
}
|
|
18125
|
+
//#endregion
|
|
18126
|
+
//#region QR code image files...
|
|
18064
18127
|
isSupportedQRCodeImageFile(fileInfo, mimeType) {
|
|
18065
18128
|
const mimeTypeToCheck = (mimeType ?? fileInfo.type ?? "").toLowerCase();
|
|
18066
18129
|
const fileName = fileInfo.name.toLowerCase();
|
|
18067
18130
|
return mimeTypeToCheck === "image/png" || mimeTypeToCheck === "image/jpeg" || mimeTypeToCheck === "image/jpg" || mimeTypeToCheck === "image/gif" || mimeTypeToCheck === "image/webp" || mimeTypeToCheck === "image/bmp" || mimeTypeToCheck === "image/svg+xml" || fileName.endsWith(".png") || fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".gif") || fileName.endsWith(".webp") || fileName.endsWith(".bmp") || fileName.endsWith(".svg");
|
|
18068
18131
|
}
|
|
18069
|
-
fileNameWithoutExtension(fileName) {
|
|
18070
|
-
const lastSeparator = Math.max(fileName.lastIndexOf("/"), fileName.lastIndexOf("\\"));
|
|
18071
|
-
const lastDot = fileName.lastIndexOf(".");
|
|
18072
|
-
if (lastDot > lastSeparator)
|
|
18073
|
-
return fileName.substring(0, lastDot);
|
|
18074
|
-
return fileName;
|
|
18075
|
-
}
|
|
18076
|
-
textFileNameForQRCodeContent(fileName, qrText) {
|
|
18077
|
-
const trimmedQRCodeText = qrText.trimStart();
|
|
18078
|
-
const baseFileName = this.fileNameWithoutExtension(fileName);
|
|
18079
|
-
if (trimmedQRCodeText.startsWith("<?xml") || trimmedQRCodeText.startsWith("<"))
|
|
18080
|
-
return baseFileName + ".xml";
|
|
18081
|
-
if (trimmedQRCodeText.startsWith("{") || trimmedQRCodeText.startsWith("["))
|
|
18082
|
-
return baseFileName + ".json";
|
|
18083
|
-
return baseFileName + ".txt";
|
|
18084
|
-
}
|
|
18085
|
-
tryExtractChargeTransparencyTextFromURL(qrText) {
|
|
18086
|
-
if (!qrText.startsWith("http://") && !qrText.startsWith("https://"))
|
|
18087
|
-
return qrText;
|
|
18088
|
-
try {
|
|
18089
|
-
const url = new URL(qrText);
|
|
18090
|
-
const candidates = [
|
|
18091
|
-
url.hash.startsWith("#") ? url.hash.substring(1) : url.hash,
|
|
18092
|
-
url.searchParams.get("data"),
|
|
18093
|
-
url.searchParams.get("content"),
|
|
18094
|
-
url.searchParams.get("ctr"),
|
|
18095
|
-
url.searchParams.get("record"),
|
|
18096
|
-
url.searchParams.get("payload"),
|
|
18097
|
-
url.searchParams.get("q")
|
|
18098
|
-
];
|
|
18099
|
-
for (const candidate of candidates) {
|
|
18100
|
-
const decodedCandidate = candidate != null ? decodeURIComponent(candidate).trim() : "";
|
|
18101
|
-
if (decodedCandidate.startsWith("<?xml") || decodedCandidate.startsWith("<values") || decodedCandidate.startsWith("{") || decodedCandidate.startsWith("[") || decodedCandidate.startsWith("OCMF") || decodedCandidate.startsWith("AP;")) {
|
|
18102
|
-
return decodedCandidate;
|
|
18103
|
-
}
|
|
18104
|
-
}
|
|
18105
|
-
} catch {
|
|
18106
|
-
console.log("Invalid URL in QR code: " + qrText);
|
|
18107
|
-
}
|
|
18108
|
-
return qrText;
|
|
18109
|
-
}
|
|
18110
|
-
tryExtractSignedDataTextFromXML(qrText) {
|
|
18111
|
-
const trimmedQRCodeText = qrText.trim();
|
|
18112
|
-
if (!trimmedQRCodeText.startsWith("<?xml") && !trimmedQRCodeText.startsWith("<"))
|
|
18113
|
-
return qrText;
|
|
18114
|
-
try {
|
|
18115
|
-
const xmlDocument = new DOMParser().parseFromString(trimmedQRCodeText, "text/xml");
|
|
18116
|
-
const signedDataValues = getElementsByLocalName(xmlDocument, "signedData").filter((signedData) => (signedData.getAttribute("format") ?? "").trim().toLowerCase() === "alfen").map((signedData) => signedData.textContent.trim()).filter((signedData) => signedData.startsWith("AP;"));
|
|
18117
|
-
if (signedDataValues.length > 0)
|
|
18118
|
-
return signedDataValues.join("\n");
|
|
18119
|
-
} catch {
|
|
18120
|
-
console.log("Error parsing XML content from QR code: " + qrText);
|
|
18121
|
-
}
|
|
18122
|
-
return qrText;
|
|
18123
|
-
}
|
|
18124
18132
|
async expandQRCodeImageFiles(FileInfos) {
|
|
18125
18133
|
const expandedFileInfos = new Array();
|
|
18126
18134
|
for (const fileInfo of FileInfos) {
|
|
@@ -18152,7 +18160,50 @@ var Chargy = class {
|
|
|
18152
18160
|
}
|
|
18153
18161
|
return expandedFileInfos;
|
|
18154
18162
|
}
|
|
18155
|
-
|
|
18163
|
+
textFileNameForQRCodeContent(fileName, qrText) {
|
|
18164
|
+
const trimmedQRCodeText = qrText.trimStart();
|
|
18165
|
+
const baseFileName = this.fileNameWithoutExtension(fileName);
|
|
18166
|
+
if (trimmedQRCodeText.startsWith("<?xml") || trimmedQRCodeText.startsWith("<"))
|
|
18167
|
+
return baseFileName + ".xml";
|
|
18168
|
+
if (trimmedQRCodeText.startsWith("{") || trimmedQRCodeText.startsWith("["))
|
|
18169
|
+
return baseFileName + ".json";
|
|
18170
|
+
return baseFileName + ".txt";
|
|
18171
|
+
}
|
|
18172
|
+
//#endregion
|
|
18173
|
+
//#region i18n...
|
|
18174
|
+
SetUILanguages(UILanguages) {
|
|
18175
|
+
this.uiLanguages = this.NormalizeUILanguages(UILanguages);
|
|
18176
|
+
}
|
|
18177
|
+
NormalizeUILanguages(UILanguages) {
|
|
18178
|
+
const languages = new Array();
|
|
18179
|
+
for (const language of UILanguages) {
|
|
18180
|
+
const normalized = language.trim();
|
|
18181
|
+
if (normalized.length > 0 && !languages.includes(normalized)) {
|
|
18182
|
+
languages.push(normalized);
|
|
18183
|
+
}
|
|
18184
|
+
}
|
|
18185
|
+
return languages.length > 0 ? languages : ["en"];
|
|
18186
|
+
}
|
|
18187
|
+
FindBestMultilanguageText(Text) {
|
|
18188
|
+
for (const language of this.uiLanguages) {
|
|
18189
|
+
const localLanguage = Text[language];
|
|
18190
|
+
if (localLanguage !== void 0)
|
|
18191
|
+
return localLanguage;
|
|
18192
|
+
}
|
|
18193
|
+
const english = Text["en"];
|
|
18194
|
+
if (english !== void 0)
|
|
18195
|
+
return english;
|
|
18196
|
+
return Object.values(Text).find((value) => value !== void 0);
|
|
18197
|
+
}
|
|
18198
|
+
CompleteMultilanguageText(Text, fallbackText) {
|
|
18199
|
+
const result = {
|
|
18200
|
+
...Text ?? {}
|
|
18201
|
+
};
|
|
18202
|
+
const fallback = this.FindBestMultilanguageText(result) ?? fallbackText;
|
|
18203
|
+
for (const language of this.uiLanguages)
|
|
18204
|
+
result[language] ??= fallback;
|
|
18205
|
+
return result;
|
|
18206
|
+
}
|
|
18156
18207
|
GetLocalizedMessage(Text) {
|
|
18157
18208
|
const multiLanguage = this.i18n[Text];
|
|
18158
18209
|
if (multiLanguage !== void 0) {
|
|
@@ -18186,6 +18237,14 @@ var Chargy = class {
|
|
|
18186
18237
|
}
|
|
18187
18238
|
return this.CompleteMultilanguageText(parameterizedText, Text.replace("%p", String(Parameter)));
|
|
18188
18239
|
}
|
|
18240
|
+
getLocalizedText(data) {
|
|
18241
|
+
if (data == null)
|
|
18242
|
+
return void 0;
|
|
18243
|
+
const bestLanguage = this.FindBestMultilanguageText(data);
|
|
18244
|
+
if (bestLanguage == null)
|
|
18245
|
+
return void 0;
|
|
18246
|
+
return data[bestLanguage];
|
|
18247
|
+
}
|
|
18189
18248
|
//#endregion
|
|
18190
18249
|
//#region GetDevices...
|
|
18191
18250
|
GetChargingPool = (Id) => {
|
|
@@ -18336,7 +18395,7 @@ var Chargy = class {
|
|
|
18336
18395
|
if (compressedFiles.length == 0)
|
|
18337
18396
|
continue;
|
|
18338
18397
|
archiveFound = true;
|
|
18339
|
-
if (compressedFiles.length == 1
|
|
18398
|
+
if (compressedFiles.length == 1) {
|
|
18340
18399
|
expandedFileInfos.push({
|
|
18341
18400
|
name: FileInfo.name.substring(0, FileInfo.name.lastIndexOf(".")),
|
|
18342
18401
|
data: compressedFiles[0]?.data
|
|
@@ -18373,9 +18432,12 @@ var Chargy = class {
|
|
|
18373
18432
|
}
|
|
18374
18433
|
if (dataFile.length > 0 && signatureFile.length > 0) {
|
|
18375
18434
|
try {
|
|
18376
|
-
|
|
18377
|
-
|
|
18378
|
-
|
|
18435
|
+
const parsedCTRFile = JSON.parse(dataFile);
|
|
18436
|
+
if (!isMandatoryJSONObject(parsedCTRFile))
|
|
18437
|
+
throw new Error("Invalid chargepoint 'secrrct' JSON file!");
|
|
18438
|
+
CTRfile = parsedCTRFile;
|
|
18439
|
+
CTRfile["original"] = btoa(dataFile);
|
|
18440
|
+
CTRfile["signature"] = signatureFile;
|
|
18379
18441
|
expandedFileInfos.push({
|
|
18380
18442
|
name: FileInfo.name,
|
|
18381
18443
|
data: new TextEncoder().encode(JSON.stringify(CTRfile))
|
|
@@ -18414,11 +18476,10 @@ var Chargy = class {
|
|
|
18414
18476
|
} while (archiveFound);
|
|
18415
18477
|
return expandedFileInfos;
|
|
18416
18478
|
}
|
|
18417
|
-
//#endregion
|
|
18418
18479
|
async extractArchive(fileName, data, mimeType) {
|
|
18419
18480
|
const archiveData = this.toUint8Array(data);
|
|
18420
18481
|
if (mimeType === "application/zip")
|
|
18421
|
-
return
|
|
18482
|
+
return this.extractZipArchive(archiveData);
|
|
18422
18483
|
if (mimeType === "application/x-tar")
|
|
18423
18484
|
return this.extractTarArchive(archiveData);
|
|
18424
18485
|
if (mimeType === "application/gzip") {
|
|
@@ -18457,7 +18518,7 @@ var Chargy = class {
|
|
|
18457
18518
|
break;
|
|
18458
18519
|
const prefix = this.readTarString(header, 345, 155);
|
|
18459
18520
|
const path = prefix.length > 0 ? prefix + "/" + name : name;
|
|
18460
|
-
const typeFlag = String.fromCharCode(header
|
|
18521
|
+
const typeFlag = String.fromCharCode(getArrayElement(header, 156, "Missing tar header type flag"));
|
|
18461
18522
|
const dataStart = offset + 512;
|
|
18462
18523
|
const dataEnd = dataStart + size;
|
|
18463
18524
|
if (dataEnd > data.byteLength)
|
|
@@ -18553,6 +18614,7 @@ var Chargy = class {
|
|
|
18553
18614
|
const value = this.readTarString(data, offset, length).replace(/\0/g, "").trim();
|
|
18554
18615
|
return value.length > 0 ? parseInt(value, 8) : 0;
|
|
18555
18616
|
}
|
|
18617
|
+
//#endregion
|
|
18556
18618
|
//#region DetectAndConvertContentFormat(FileInfos)
|
|
18557
18619
|
async DetectAndConvertContentFormat(FileInfos) {
|
|
18558
18620
|
if (FileInfos.length == 0) return {
|
|
@@ -18566,45 +18628,48 @@ var Chargy = class {
|
|
|
18566
18628
|
for (const fileInfo of FileInfos) {
|
|
18567
18629
|
if (fileInfo.type === "application/pdf" || fileInfo.name.endsWith(".pdf")) {
|
|
18568
18630
|
const pdfjsLib = await importPdfJs();
|
|
18569
|
-
const pdfDocument = fileInfo.data ? await pdfjsLib.getDocument({ data: fileInfo.data }).promise : fileInfo.path ? await pdfjsLib.getDocument({ url: fileInfo.path }).promise : null;
|
|
18570
|
-
if (pdfDocument) {
|
|
18631
|
+
const pdfDocument = fileInfo.data ? await pdfjsLib.getDocument({ data: fileInfo.data }).promise : fileInfo.path != null && fileInfo.path.length > 0 ? await pdfjsLib.getDocument({ url: fileInfo.path }).promise : null;
|
|
18632
|
+
if (pdfDocument !== null) {
|
|
18571
18633
|
try {
|
|
18572
|
-
const
|
|
18573
|
-
|
|
18574
|
-
|
|
18575
|
-
|
|
18576
|
-
|
|
18577
|
-
|
|
18578
|
-
|
|
18579
|
-
|
|
18580
|
-
|
|
18581
|
-
|
|
18582
|
-
|
|
18583
|
-
|
|
18584
|
-
|
|
18585
|
-
|
|
18586
|
-
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18590
|
-
|
|
18591
|
-
|
|
18592
|
-
|
|
18593
|
-
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
|
|
18597
|
-
|
|
18598
|
-
|
|
18599
|
-
|
|
18600
|
-
|
|
18601
|
-
|
|
18602
|
-
|
|
18603
|
-
|
|
18604
|
-
|
|
18605
|
-
|
|
18606
|
-
|
|
18607
|
-
|
|
18634
|
+
const attachmentsUnknown = await pdfDocument.getAttachments();
|
|
18635
|
+
if (isMandatoryJSONObject(attachmentsUnknown))
|
|
18636
|
+
Object.values(attachmentsUnknown).forEach((attachmentUnknown) => {
|
|
18637
|
+
if (!isPdfAttachment(attachmentUnknown))
|
|
18638
|
+
return;
|
|
18639
|
+
const attachment = attachmentUnknown;
|
|
18640
|
+
if (attachment.filename.endsWith(".chargy"))
|
|
18641
|
+
expandedFiles.push({
|
|
18642
|
+
name: attachment.filename,
|
|
18643
|
+
path: FileInfos[0]?.path,
|
|
18644
|
+
type: "application/chargy",
|
|
18645
|
+
data: attachment.content,
|
|
18646
|
+
info: "A CHARGY file extracted from a PDF/A-3 or newer attachment"
|
|
18647
|
+
});
|
|
18648
|
+
else if (attachment.filename.endsWith(".xml"))
|
|
18649
|
+
expandedFiles.push({
|
|
18650
|
+
name: attachment.filename,
|
|
18651
|
+
path: FileInfos[0]?.path,
|
|
18652
|
+
type: "application/xml",
|
|
18653
|
+
data: attachment.content,
|
|
18654
|
+
info: "A XML file extracted from a PDF/A-3 or newer attachment"
|
|
18655
|
+
});
|
|
18656
|
+
else if (attachment.filename.endsWith(".json"))
|
|
18657
|
+
expandedFiles.push({
|
|
18658
|
+
name: attachment.filename,
|
|
18659
|
+
path: FileInfos[0]?.path,
|
|
18660
|
+
type: "application/json",
|
|
18661
|
+
data: attachment.content,
|
|
18662
|
+
info: "A JSON file extracted from a PDF/A-3 or newer attachment"
|
|
18663
|
+
});
|
|
18664
|
+
else if (attachment.filename.endsWith(".csv"))
|
|
18665
|
+
expandedFiles.push({
|
|
18666
|
+
name: attachment.filename,
|
|
18667
|
+
path: FileInfos[0]?.path,
|
|
18668
|
+
type: "text/csv",
|
|
18669
|
+
data: attachment.content,
|
|
18670
|
+
info: "A CSV file extracted from a PDF/A-3 or newer attachment"
|
|
18671
|
+
});
|
|
18672
|
+
});
|
|
18608
18673
|
} catch (error) {
|
|
18609
18674
|
console.error(`Error extracting PDF/A-3 attachments: ${String(error)}`);
|
|
18610
18675
|
}
|
|
@@ -18650,7 +18715,7 @@ var Chargy = class {
|
|
|
18650
18715
|
break;
|
|
18651
18716
|
}
|
|
18652
18717
|
processedFile.result = await new SAFEXML(this).tryToParseSAFEXML(XMLDocument);
|
|
18653
|
-
if (processedFile.result.status && processedFile.result.status !== "Unvalidated" /* Unvalidated */ && getElementsByLocalName(XMLDocument, "chargingStation").length === 0) {
|
|
18718
|
+
if (processedFile.result.status != null && processedFile.result.status !== "Unvalidated" /* Unvalidated */ && getElementsByLocalName(XMLDocument, "chargingStation").length === 0) {
|
|
18654
18719
|
processedFile.result = new XMLContainer(this).tryToParseXMLContainer(XMLDocument);
|
|
18655
18720
|
}
|
|
18656
18721
|
break;
|
|
@@ -18734,8 +18799,7 @@ var Chargy = class {
|
|
|
18734
18799
|
if (isMandatoryJSONObject(JSONContent)) {
|
|
18735
18800
|
const JSONContext = JSONContent["@context"];
|
|
18736
18801
|
if (IsAChargeTransparencyLiveLink(JSONContent)) {
|
|
18737
|
-
|
|
18738
|
-
JSONContent.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
18802
|
+
JSONContent.timestamp ??= (/* @__PURE__ */ new Date()).toISOString();
|
|
18739
18803
|
processedFile.result = JSONContent;
|
|
18740
18804
|
} else if (isMandatoryString(JSONContext)) {
|
|
18741
18805
|
if (JSONContext.startsWith("https://open.charging.cloud/contexts/CTR+json"))
|
|
@@ -18779,25 +18843,18 @@ var Chargy = class {
|
|
|
18779
18843
|
processedFiles.push(processedFile);
|
|
18780
18844
|
}
|
|
18781
18845
|
if (processedFiles.length == 1) {
|
|
18782
|
-
const processedFile = processedFiles
|
|
18783
|
-
if (processedFile)
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
if (IsAChargeTransparencyLiveLink(processedFile.result))
|
|
18787
|
-
return processedFile.result;
|
|
18788
|
-
if (IsAPublicKeyLookup(processedFile.result))
|
|
18789
|
-
return {
|
|
18790
|
-
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
18791
|
-
message: this.GetMultilanguageText("UnknownOrInvalidChargeTransparencyRecord"),
|
|
18792
|
-
certainty: 0
|
|
18793
|
-
};
|
|
18846
|
+
const processedFile = getFirstArrayElement(processedFiles, "Missing processed file");
|
|
18847
|
+
if (IsAChargeTransparencyRecord(processedFile.result))
|
|
18848
|
+
return this.processChargeTransparencyRecord(processedFile.result);
|
|
18849
|
+
if (IsAChargeTransparencyLiveLink(processedFile.result))
|
|
18794
18850
|
return processedFile.result;
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
|
|
18799
|
-
|
|
18800
|
-
|
|
18851
|
+
if (IsAPublicKeyLookup(processedFile.result))
|
|
18852
|
+
return {
|
|
18853
|
+
status: "InvalidSessionFormat" /* InvalidSessionFormat */,
|
|
18854
|
+
message: this.GetMultilanguageText("UnknownOrInvalidChargeTransparencyRecord"),
|
|
18855
|
+
certainty: 0
|
|
18856
|
+
};
|
|
18857
|
+
return processedFile.result;
|
|
18801
18858
|
} else if (processedFiles.length > 1) {
|
|
18802
18859
|
const mergedCTR = {
|
|
18803
18860
|
"@id": "",
|
|
@@ -18811,14 +18868,12 @@ var Chargy = class {
|
|
|
18811
18868
|
mergedCTR["@id"] = processedFileResult["@id"];
|
|
18812
18869
|
if (mergedCTR["@context"] === "")
|
|
18813
18870
|
mergedCTR["@context"] = processedFileResult["@context"];
|
|
18814
|
-
if (
|
|
18871
|
+
if (processedFileResult.begin != null && processedFileResult.begin.length > 0 && (mergedCTR.begin == null || mergedCTR.begin.length === 0 || mergedCTR.begin > processedFileResult.begin))
|
|
18815
18872
|
mergedCTR.begin = processedFileResult.begin;
|
|
18816
|
-
if (
|
|
18873
|
+
if (processedFileResult.end != null && processedFileResult.end.length > 0 && (mergedCTR.end == null || mergedCTR.end.length === 0 || mergedCTR.end < processedFileResult.end))
|
|
18817
18874
|
mergedCTR.end = processedFileResult.end;
|
|
18818
|
-
|
|
18819
|
-
|
|
18820
|
-
if (!mergedCTR.contract)
|
|
18821
|
-
mergedCTR.contract = processedFileResult.contract;
|
|
18875
|
+
mergedCTR.description ??= processedFileResult.description;
|
|
18876
|
+
mergedCTR.contract ??= processedFileResult.contract;
|
|
18822
18877
|
if (!mergedCTR.chargingStationOperators)
|
|
18823
18878
|
mergedCTR.chargingStationOperators = processedFileResult.chargingStationOperators;
|
|
18824
18879
|
else if (processedFileResult.chargingStationOperators)
|
|
@@ -18850,17 +18905,14 @@ var Chargy = class {
|
|
|
18850
18905
|
for (const mediationService of processedFileResult.mediationServices)
|
|
18851
18906
|
mergedCTR.mediationServices.push(mediationService);
|
|
18852
18907
|
} else if (IsAPublicKeyInfo(processedFileResult)) {
|
|
18853
|
-
|
|
18854
|
-
mergedCTR.publicKeys = new Array();
|
|
18908
|
+
mergedCTR.publicKeys ??= new Array();
|
|
18855
18909
|
mergedCTR.publicKeys.push(processedFileResult);
|
|
18856
18910
|
} else if (IsAPublicKeyLookup(processedFileResult)) {
|
|
18857
|
-
|
|
18858
|
-
mergedCTR.publicKeys = new Array();
|
|
18911
|
+
mergedCTR.publicKeys ??= new Array();
|
|
18859
18912
|
for (const publicKey of processedFileResult.publicKeys)
|
|
18860
18913
|
mergedCTR.publicKeys.push(publicKey);
|
|
18861
18914
|
} else {
|
|
18862
|
-
|
|
18863
|
-
mergedCTR.invalidDataSets = new Array();
|
|
18915
|
+
mergedCTR.invalidDataSets ??= new Array();
|
|
18864
18916
|
mergedCTR.invalidDataSets.push(processedFile);
|
|
18865
18917
|
}
|
|
18866
18918
|
}
|
|
@@ -18888,8 +18940,6 @@ var Chargy = class {
|
|
|
18888
18940
|
this.EVSEs = [];
|
|
18889
18941
|
this.meters = [];
|
|
18890
18942
|
this.chargingSessions = [];
|
|
18891
|
-
this.eMobilityProviders = [];
|
|
18892
|
-
this.mediationServices = [];
|
|
18893
18943
|
try {
|
|
18894
18944
|
this.internalCTR = CloneCTR(CTR);
|
|
18895
18945
|
this.currentCTR = CTR;
|
|
@@ -19016,9 +19066,9 @@ var Chargy = class {
|
|
|
19016
19066
|
if (measurement.name !== "ENERGY_TOTAL" || measurement.values.length < 2) {
|
|
19017
19067
|
continue;
|
|
19018
19068
|
}
|
|
19019
|
-
const firstValue2 = measurement.values
|
|
19020
|
-
const lastValue = measurement.values
|
|
19021
|
-
if (firstValue2
|
|
19069
|
+
const firstValue2 = measurement.values.at(0);
|
|
19070
|
+
const lastValue = measurement.values.at(-1);
|
|
19071
|
+
if (firstValue2 === void 0 || lastValue === void 0) {
|
|
19022
19072
|
continue;
|
|
19023
19073
|
}
|
|
19024
19074
|
const firstValueKWh = this.measurementValueInKWh(measurement, firstValue2);
|
|
@@ -19167,14 +19217,12 @@ var Chargy = class {
|
|
|
19167
19217
|
mergedCTR["@id"] = ctr["@id"];
|
|
19168
19218
|
if (mergedCTR["@context"] === "")
|
|
19169
19219
|
mergedCTR["@context"] = ctr["@context"];
|
|
19170
|
-
if (
|
|
19220
|
+
if (ctr.begin != null && ctr.begin.length > 0 && (mergedCTR.begin == null || mergedCTR.begin.length === 0 || mergedCTR.begin > ctr.begin))
|
|
19171
19221
|
mergedCTR.begin = ctr.begin;
|
|
19172
|
-
if (
|
|
19222
|
+
if (ctr.end != null && ctr.end.length > 0 && (mergedCTR.end == null || mergedCTR.end.length === 0 || mergedCTR.end < ctr.end))
|
|
19173
19223
|
mergedCTR.end = ctr.end;
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
if (!mergedCTR.contract)
|
|
19177
|
-
mergedCTR.contract = ctr.contract;
|
|
19224
|
+
mergedCTR.description ??= ctr.description;
|
|
19225
|
+
mergedCTR.contract ??= ctr.contract;
|
|
19178
19226
|
if (!mergedCTR.chargingStationOperators)
|
|
19179
19227
|
mergedCTR.chargingStationOperators = ctr.chargingStationOperators;
|
|
19180
19228
|
else if (ctr.chargingStationOperators)
|
|
@@ -19235,6 +19283,6 @@ buffer/index.js:
|
|
|
19235
19283
|
*)
|
|
19236
19284
|
*/
|
|
19237
19285
|
|
|
19238
|
-
export { ACrypt, Alfen, AlfenCrypt01, BSMCrypt01, CanonicalJSONError, ChargeIT, ChargePoint, ChargePointCrypt01, IChargeTransparencyLiveLink_exports as ChargeTransparencyLiveLink, ChargeTransparencyLiveLinkContext, IChargeTransparencyRecord_exports as ChargeTransparencyRecord, Chargy, chargyInterfaces_exports as ChargyInterfaces, Clone, CloneCTR, ConcatenateBuffers, CreateDiv, CreateDiv2, CreateError, CryptoAlgorithms, CryptoHashAlgorithms, DayOfWeek, DisplayPrefixes, EMHCrypt01, ErrorLevel, GDFCrypt01, IECCurves, IEncoding, InformationRelevance, InformationRelevanceToString, IsAChargeTransparencyLiveLink, IsAChargeTransparencyRecord, IsAPublicKeyInfo, IsAPublicKeyLookup, IsASessionCryptoResult, IsNullOrEmpty, JSONSignatureVerificationStatus, MENNEKES_EDL40_OBIS, MENNEKES_EDL40_XMLNS, Mennekes, MennekesCrypt01, OBIS2Hex, OBIS2MeasurementName, OBIS_RegExpr, OCMF, OCMFTransactionTypes, OCMFv1_x, OCPI, PCDF, PCDFCrypt01, PCDFParseError, PCDFValidationError, PCDF_FIELD_ORDER, PCDF_PREFIX, ParseJSON_LD, PublicKeyFormats, IPublicKeyInfo_exports as PublicKeyInfo, SAFEXML, SessionVerificationResult, SetHex, SetInt8, SetText, SetText_withLength, SetTimestamp, SetTimestamp32, SetUInt32, SetUInt32_withCode, SetUInt64, SetUInt64D, SignMessage, SignatureFormats, TimeStatusTypes, UTC2human, VerificationResult, VerifyJSONMessageSignatures, WarningLevel, WhenNullOrEmpty, XMLContainer, asJSONArray, asJSONObject, asNumber, asString, buf2hex, buildMennekesSignatureData, bytesToHex2 as bytesToHex, canonicalJSONBytes, canonicalJSONStringify, cleanHex, closeFullscreen, createHexString, dateToMennekesLocalEpochSeconds, extractMennekesChargingProcesses, firstKey, firstValue, getDirectChildByLocalName, getDirectChildrenByLocalName, getElementsByLocalName, getInt16Bytes, getInt32Bytes, getInt64Bytes, getInt8Bytes, getTrimmedTextContent, hashFile, hex2bin, hex32, hexToArrayBuffer, hexToBytes, intFromBytes, isGeoLocation, isICryptoResult, isIFileInfo, isIPublicKeyXY, isISessionCryptoResult1, isISessionCryptoResult2, isJSONLDObject, isMandatoryArrayOfStrings, isMandatoryBoolean, isMandatoryDecimal, isMandatoryJSONArray, isMandatoryJSONObject, isMandatoryNumber, isMandatoryString, isMandatoryURL, isMultilanguageText, isOIDInfo, isObject, isOptionalArrayOfStrings, isOptionalDecimal, isOptionalJSONArray, isOptionalJSONArrayError, isOptionalJSONArrayOk, isOptionalJSONObject, isOptionalNumber, isOptionalString, isOptionalURL, isPCDFText, isString, isStringArray, isStringOrOIDInfo, isStringOrStringArray, jsonPrettyPrinter, measurementName2human, normalizePCDFPublicKeyHex, normalizeXMLText, openFullscreen, pad, parseAndVerifyJSONSignatures, parseDescription, parseHexString, parseMennekesXMLDocument, parseNumber, parseOBIS, parsePCDFDocument, parsePCDFPublicKey, parsePCDFSignature, parseUTC, readQRCodeTextFromImage, readQRCodeTextFromImageData, secp224k1, setUILocale, sha256, sha384, sha512, signJSONMessage, signMessage, stripPCDFControlCharacters, time2human, toArrayBuffer, toSessionVerificationResults, toUint8Array, unquotePCDFText, validatePCDFFields, verifyJSONMessageSignatureResults, verifyJSONMessageSignatures, verifyJSONSignature, verifyJSONSignatureResult, verifyPCDFDocument };
|
|
19286
|
+
export { ACrypt, Alfen, AlfenCrypt01, BSMCrypt01, CanonicalJSONError, ChargeIT, ChargePoint, ChargePointCrypt01, IChargeTransparencyLiveLink_exports as ChargeTransparencyLiveLink, ChargeTransparencyLiveLinkContext, IChargeTransparencyRecord_exports as ChargeTransparencyRecord, Chargy, chargyInterfaces_exports as ChargyInterfaces, Clone, CloneCTR, ConcatenateBuffers, CreateDiv, CreateDiv2, CreateError, CryptoAlgorithms, CryptoHashAlgorithms, DayOfWeek, DisplayPrefixes, EMHCrypt01, ErrorLevel, GDFCrypt01, IECCurves, IEncoding, InformationRelevance, InformationRelevanceToString, IsAChargeTransparencyLiveLink, IsAChargeTransparencyRecord, IsAPublicKeyInfo, IsAPublicKeyLookup, IsASessionCryptoResult, IsNullOrEmpty, JSONSignatureVerificationStatus, MENNEKES_EDL40_OBIS, MENNEKES_EDL40_XMLNS, Mennekes, MennekesCrypt01, OBIS2Hex, OBIS2MeasurementName, OBIS_RegExpr, OCMF, OCMFTransactionTypes, OCMFv1_x, OCPI, PCDF, PCDFCrypt01, PCDFParseError, PCDFValidationError, PCDF_FIELD_ORDER, PCDF_PREFIX, ParseJSON_LD, PublicKeyFormats, IPublicKeyInfo_exports as PublicKeyInfo, SAFEXML, SessionVerificationResult, SetHex, SetInt8, SetText, SetText_withLength, SetTimestamp, SetTimestamp32, SetUInt32, SetUInt32_withCode, SetUInt64, SetUInt64D, SignMessage, SignatureFormats, TimeStatusTypes, UTC2human, VerificationResult, VerifyJSONMessageSignatures, WarningLevel, WhenNullOrEmpty, XMLContainer, asJSONArray, asJSONObject, asNumber, asString, buf2hex, buildMennekesSignatureData, bytesToHex2 as bytesToHex, canonicalJSONBytes, canonicalJSONStringify, cleanHex, closeFullscreen, createHexString, dateToMennekesLocalEpochSeconds, extractMennekesChargingProcesses, firstKey, firstValue, getArrayElement, getArrayLikeElement, getDirectChildByLocalName, getDirectChildrenByLocalName, getElementsByLocalName, getFirstArrayElement, getInt16Bytes, getInt32Bytes, getInt64Bytes, getInt8Bytes, getLastArrayElement, getTrimmedTextContent, hashFile, hex2bin, hex32, hexToArrayBuffer, hexToBytes, intFromBytes, isGeoLocation, isICryptoResult, isIFileInfo, isIPublicKeyXY, isISessionCryptoResult1, isISessionCryptoResult2, isJSONLDObject, isMandatoryArrayOfStrings, isMandatoryBoolean, isMandatoryDecimal, isMandatoryJSONArray, isMandatoryJSONObject, isMandatoryNumber, isMandatoryString, isMandatoryURL, isMultilanguageText, isOIDInfo, isObject, isOptionalArrayOfStrings, isOptionalDecimal, isOptionalJSONArray, isOptionalJSONArrayError, isOptionalJSONArrayOk, isOptionalJSONObject, isOptionalNumber, isOptionalString, isOptionalURL, isPCDFText, isString, isStringArray, isStringOrOIDInfo, isStringOrStringArray, jsonPrettyPrinter, measurementName2human, normalizePCDFPublicKeyHex, normalizeXMLText, openFullscreen, pad, parseAndVerifyJSONSignatures, parseDescription, parseHexString, parseMennekesXMLDocument, parseNumber, parseOBIS, parsePCDFDocument, parsePCDFPublicKey, parsePCDFSignature, parseUTC, readQRCodeTextFromImage, readQRCodeTextFromImageData, secp224k1, setUILocale, sha256, sha384, sha512, signJSONMessage, signMessage, stripPCDFControlCharacters, time2human, toArrayBuffer, toSessionVerificationResults, toUint8Array, unquotePCDFText, validatePCDFFields, verifyJSONMessageSignatureResults, verifyJSONMessageSignatures, verifyJSONSignature, verifyJSONSignatureResult, verifyPCDFDocument };
|
|
19239
19287
|
//# sourceMappingURL=index.js.map
|
|
19240
19288
|
//# sourceMappingURL=index.js.map
|