Skip to content

Commit

Permalink
refactor(td-tools): AID idShort does not allow ":" and used "_" inste…
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpeintner authored Oct 13, 2023
1 parent 7a99bf9 commit 63b31c6
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 16 deletions.
20 changes: 13 additions & 7 deletions packages/td-tools/src/util/asset-interface-description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,14 @@ export class AssetInterfaceDescriptionUtil {
form.href = v.value;
}
} else if (typeof v.idShort === "string" && v.idShort.length > 0) {
// TODO is this still relevant?
// pick *any* value (and possibly override, e.g. contentType)
// TODO Should we add all value's (e.g., dataMapping might be empty array) ?
// if (typeof v.value === "string" ||typeof v.value === "number" || typeof v.value === "boolean") {
if (v.value != null) {
form[v.idShort] = v.value;
// Note: AID does not allow idShort to contain values with colon (i.e., ":") --> "_" used instead
// --> THIS MAY LEAD TO PROBLEMS BUT THAT'S HOW IT IS SPECIFIED
const tdTerm = (v.idShort as string).replace("_", ":");
form[tdTerm] = v.value;
// use valueType to convert the string value
// TODO Should we add/support all value's (e.g., dataMapping might be empty array) ?
if (
v.valueType != null &&
v.valueType.dataObjectType != null &&
Expand All @@ -364,7 +365,7 @@ export class AssetInterfaceDescriptionUtil {
// XSD schemaTypes, https://www.w3.org/TR/xmlschema-2/#built-in-datatypes
switch (v.valueType.dataObjectType.name) {
case "boolean":
form[v.idShort] = form[v.idShort] === "true";
form[tdTerm] = form[v.value] === "true";
break;
case "float":
case "double":
Expand All @@ -382,7 +383,7 @@ export class AssetInterfaceDescriptionUtil {
case "unsignedShort":
case "unsignedByte":
case "positiveInteger":
form[v.idShort] = Number(form[v.idShort]);
form[tdTerm] = Number(form[v.value]);
break;
// TODO handle more XSD types ?
}
Expand Down Expand Up @@ -836,8 +837,13 @@ export class AssetInterfaceDescriptionUtil {
// --> pick the first one that matches protocol (other means in future?)

// walk over string values like: "href", "contentType", "htv:methodName", ...
for (const formTerm in formElementPicked) {
for (let formTerm in formElementPicked) {
const formValue = formElementPicked[formTerm];

// Note: AID does not allow idShort to contain values with colon (i.e., ":") --> "_" used instead
// TODO are there more characters we need to deal with?
formTerm = formTerm.replace(":", "_");

if (typeof formValue === "string") {
propertyForm.push({
idShort: formTerm,
Expand Down
160 changes: 153 additions & 7 deletions packages/td-tools/test/AssetInterfaceDescriptionTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,157 @@ class AssetInterfaceDescriptionUtilTest {
expect(tdObj.properties.device_name.forms[0]).not.to.have.property("security");
}

@test async "should correctly roundtrip inverterModbus from/to AID"() {
const aasInput = (await fs.readFile("test/util/inverterModbus.json")).toString();
const td = this.assetInterfaceDescriptionUtil.transformAAS2TD(aasInput);

const aidOutput = this.assetInterfaceDescriptionUtil.transformTD2SM(td);

const smObj = JSON.parse(aidOutput);
expect(smObj).to.have.property("idShort").that.equals("AssetInterfacesDescription");
expect(smObj).to.have.property("submodelElements").to.be.an("array").to.have.lengthOf.greaterThan(0);
const smInterface = smObj.submodelElements[0];
expect(smInterface).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0);
let hasThingTitle = false;
let hasEndpointMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "title") {
hasThingTitle = true;
expect(smValue).to.have.property("value").to.equal("Inverter GEN44");
} else if (smValue.idShort === "EndpointMetadata") {
hasEndpointMetadata = true;
const endpointMetadata = smValue;
expect(endpointMetadata).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0);
let hasSecurity = false;
let hasSecurityDefinitions = false;
for (const endpointMetadataValue of endpointMetadata.value) {
if (endpointMetadataValue.idShort === "security") {
hasSecurity = true;
expect(endpointMetadataValue)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
expect(endpointMetadataValue.value[0].value).to.equal("nosec_sc");
} else if (endpointMetadataValue.idShort === "securityDefinitions") {
hasSecurityDefinitions = true;
expect(endpointMetadataValue)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasBasicSC = false;
for (const securityDefinitionValue of endpointMetadataValue.value) {
if (securityDefinitionValue.idShort === "nosec_sc") {
hasBasicSC = true;
expect(securityDefinitionValue)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasBasic = false;
for (const sec of securityDefinitionValue.value) {
if (sec.idShort === "scheme") {
hasBasic = true;
expect(sec.value).to.equal("nosec");
}
}
expect(hasBasic).to.equal(true);
}
}
expect(hasBasicSC).to.equal(true);
}
}
expect(hasSecurity).to.equal(true);
expect(hasSecurityDefinitions).to.equal(true);
}
}
expect(hasThingTitle, "No thing title").to.equal(true);
expect(hasEndpointMetadata, "No EndpointMetadata").to.equal(true);

// InterfaceMetadata with properties etc
let hasInterfaceMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "InterfaceMetadata") {
hasInterfaceMetadata = true;
expect(smValue).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0);
let hasProperties = false;
for (const interactionValues of smValue.value) {
if (interactionValues.idShort === "properties") {
hasProperties = true;
expect(interactionValues)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasPropertyDeviceName = false;
for (const propertyValue of interactionValues.value) {
if (propertyValue.idShort === "device_name") {
hasPropertyDeviceName = true;
expect(propertyValue)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasType = false;
let hasTitle = false;
let hasForms = false;
for (const propProperty of propertyValue.value) {
if (propProperty.idShort === "type") {
hasType = true;
expect(propProperty.value).to.equal("string");
} else if (propProperty.idShort === "title") {
hasTitle = true;
expect(propProperty.value).to.equal("Device name");
} else if (propProperty.idShort === "forms") {
hasForms = true;
expect(propProperty)
.to.have.property("value")
.to.be.an("array")
.to.have.lengthOf.greaterThan(0);
let hasHref = false;
let hasOp = false;
let hasContentType = false;
let hasModbusFunction = false;
let hasModbusType = false;
for (const formEntry of propProperty.value) {
if (formEntry.idShort === "href") {
hasHref = true;
expect(formEntry.value).to.equal(
"modbus+tcp://192.168.178.146:502/1/40020?quantity=16"
);
} else if (formEntry.idShort === "op") {
hasOp = true;
expect(formEntry.value).to.equal("readproperty");
} else if (formEntry.idShort === "contentType") {
hasContentType = true;
expect(formEntry.value).to.equal("application/octet-stream");
} else if (formEntry.idShort === "modbus_function") {
// vs. "modbus:function"
hasModbusFunction = true;
expect(formEntry.value).to.equal("readHoldingRegisters");
} else if (formEntry.idShort === "modbus_type") {
// vs. "modbus:type"
hasModbusType = true;
expect(formEntry.value).to.equal("string");
}
}
expect(hasHref).to.equal(true);
expect(hasOp).to.equal(true);
expect(hasContentType).to.equal(true);
expect(hasModbusFunction).to.equal(true);
expect(hasModbusType).to.equal(true);
}
}
expect(hasType).to.equal(true);
expect(hasTitle).to.equal(true);
expect(hasForms).to.equal(true);
}
}
expect(hasPropertyDeviceName).to.equal(true);
}
}
expect(hasProperties).to.equal(true);
}
}
expect(hasInterfaceMetadata, "No InterfaceMetadata").to.equal(true);
}

td1Base = "https://www.example.com/";
td1: ThingDescription = {
"@context": "https://www.w3.org/2022/wot/td/v1.1",
Expand Down Expand Up @@ -242,7 +393,7 @@ class AssetInterfaceDescriptionUtilTest {
},
};

@test async "should correctly transform sample TD into JSON submodel"() {
@test async "should correctly transform sample TD into AID submodel"() {
const sm = this.assetInterfaceDescriptionUtil.transformTD2SM(JSON.stringify(this.td1), ["https"]);

const smObj = JSON.parse(sm);
Expand Down Expand Up @@ -358,7 +509,6 @@ class AssetInterfaceDescriptionUtilTest {
let hasHref = false;
let hasContentType = false;
let hasHtvMethodName = false;
// let hasOp = false;
for (const formEntry of propProperty.value) {
if (formEntry.idShort === "href") {
hasHref = true;
Expand All @@ -369,18 +519,14 @@ class AssetInterfaceDescriptionUtilTest {
} else if (formEntry.idShort === "contentType") {
hasContentType = true;
expect(formEntry.value).to.equal("application/json");
} else if (formEntry.idShort === "htv:methodName") {
} else if (formEntry.idShort === "htv_methodName") {
hasHtvMethodName = true;
expect(formEntry.value).to.equal("GET");
// } else if (formEntry.idShort === "op") {
// hasOp = true;
// expect(formEntry.value).to.have.members(["readproperty"]);
}
}
expect(hasHref).to.equal(true);
expect(hasContentType).to.equal(true);
expect(hasHtvMethodName).to.equal(true);
// expect(hasOp).to.equal(true);
}
}
expect(hasType).to.equal(true);
Expand Down
4 changes: 2 additions & 2 deletions packages/td-tools/test/util/inverterModbus.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@
"modelType": "Property"
},
{
"idShort": "modbus:function",
"idShort": "modbus_function",
"valueType": "xs:string",
"value": "readHoldingRegisters",
"modelType": "Property"
},
{
"idShort": "modbus:type",
"idShort": "modbus_type",
"valueType": "xs:string",
"value": "string",
"modelType": "Property"
Expand Down

0 comments on commit 63b31c6

Please sign in to comment.