Skip to content

Commit

Permalink
Update Asset Interfaces Description (AID) submodel (#1222)
Browse files Browse the repository at this point in the history
* refactor: update revised SemanticIDs

* refactor: rename InterfaceMetadata to InteractionMetadata

* refactor: change modbus to modv

* refactor: add 2 new terms mostSignificantByte and mostSignificantWord for Modbus

* refactor: update JSON schema with modbusMostSignificantByteElement and modbusMostSignificantWordElement
  • Loading branch information
danielpeintner authored Jan 23, 2024
1 parent c130878 commit 33be732
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 109 deletions.
52 changes: 28 additions & 24 deletions packages/td-tools/src/util/asset-interface-description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export class AssetInterfaceDescriptionUtil {
},
// created, modified, support ?
this.createEndpointMetadata(td, protocol, aidID, submodelElementIdShort), // EndpointMetadata like base, security and securityDefinitions
this.createInterfaceMetadata(td, protocol), // InterfaceMetadata like properties, actions and events
this.createInteractionMetadata(td, protocol), // InteractionMetadata like properties, actions and events
// Note: "ExternalDescriptor" should contain file values --> not applicable to TD
/* {
idShort: "ExternalDescriptor",
Expand Down Expand Up @@ -532,7 +532,7 @@ export class AssetInterfaceDescriptionUtil {
}

private processSubmodelElement(smInformation: SubmodelInformation, submodelElement: Record<string, unknown>): void {
// EndpointMetadata vs. InterfaceMetadata
// EndpointMetadata vs. InteractionMetadata
if (submodelElement.value instanceof Array) {
// Note: iterate twice over to collect first EndpointMetadata
let endpointMetadata: Record<string, unknown> = {};
Expand All @@ -543,7 +543,7 @@ export class AssetInterfaceDescriptionUtil {
// e.g., idShort: base , contentType, securityDefinitions, alternativeEndpointDescriptor?
endpointMetadata = smValue;
smInformation.endpointMetadataArray.push(endpointMetadata);
} else if (smValue.idShort === "InterfaceMetadata") {
} else if (smValue.idShort === "InteractionMetadata") {
// handled later
} else if (smValue.idShort === "externalDescriptor") {
// needed?
Expand All @@ -552,11 +552,11 @@ export class AssetInterfaceDescriptionUtil {
}
}
}
// the 2nd time look for InterfaceMetadata that *need* EndpointMetadata
// the 2nd time look for InteractionMetadata that *need* EndpointMetadata
for (const smValue of submodelElement.value) {
if (smValue instanceof Object) {
if (smValue.idShort === "InterfaceMetadata") {
logInfo("InterfaceMetadata");
if (smValue.idShort === "InteractionMetadata") {
logInfo("InteractionMetadata");
if (smValue.value instanceof Array) {
for (const interactionValue of smValue.value) {
if (interactionValue.idShort === "properties") {
Expand Down Expand Up @@ -929,7 +929,7 @@ export class AssetInterfaceDescriptionUtil {
// scheme always
values.push({
idShort: "scheme",
semanticId: this.createSemanticId("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
semanticId: this.createSemanticId("https://www.w3.org/2019/wot/security#SecurityScheme"),
valueType: "xs:string",
value: secValue.scheme,
modelType: "Property",
Expand Down Expand Up @@ -1122,9 +1122,9 @@ export class AssetInterfaceDescriptionUtil {
}
}
} else if (protocol.startsWith("modbus")) {
// Modbus: href, modbus_function
// default for modbus:function depending on op, see https://w3c.github.io/wot-binding-templates/bindings/protocols/modbus/index.html#default-mappings
const mbKey = "modbus:function";
// Modbus: href, modv_function
// default for modv:function depending on op, see https://w3c.github.io/wot-binding-templates/bindings/protocols/modbus/index.html#default-mappings
const mbKey = "modv:function";
if (form[mbKey] == null) {
if (this.hasOp(form, "writeproperty") || this.hasOp(form, "invokeaction")) {
form[mbKey] = "writeSingleCoil";
Expand Down Expand Up @@ -1161,7 +1161,7 @@ export class AssetInterfaceDescriptionUtil {
}
}

private createInterfaceMetadata(td: ThingDescription, protocol: string): Record<string, unknown> {
private createInteractionMetadata(td: ThingDescription, protocol: string): Record<string, unknown> {
const properties: Array<unknown> = [];
const actions: Array<unknown> = [];
const events: Array<unknown> = [];
Expand Down Expand Up @@ -1372,18 +1372,22 @@ export class AssetInterfaceDescriptionUtil {
semanticId = "https://www.w3.org/2011/http#fieldName";
} else if (formTerm === "htv:fieldValue") {
semanticId = "https://www.w3.org/2011/http#fieldValue";
} else if (formTerm === "modbus:function") {
semanticId = "https://www.w3.org/2019/wot/modbus#Function";
} else if (formTerm === "modbus:entity") {
semanticId = "https://www.w3.org/2019/wot/modbus#Entity";
} else if (formTerm === "modbus:zeroBasedAddressing") {
} else if (formTerm === "modv:function") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasFunction";
} else if (formTerm === "modv:entity") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasEntity";
} else if (formTerm === "modv:zeroBasedAddressing") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasZeroBasedAddressingFlag";
} else if (formTerm === "modbus:timeout") {
} else if (formTerm === "modv:timeout") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasTimeout";
} else if (formTerm === "modbus:pollingTime") {
} else if (formTerm === "modv:pollingTime") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasPollingTime";
} else if (formTerm === "modbus:type") {
semanticId = "https://www.w3.org/2019/wot/modbus#type";
} else if (formTerm === "modv:type") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasPayloadDataType";
} else if (formTerm === "modv:mostSignificantByte") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasMostSignificantByte";
} else if (formTerm === "modv:mostSignificantWord") {
semanticId = "https://www.w3.org/2019/wot/modbus#hasMostSignificantWord";
} else if (formTerm === "mqv:retain") {
semanticId = "https://www.w3.org/2019/wot/mqtt#hasRetainFlag";
} else if (formTerm === "mqv:controlPacket") {
Expand Down Expand Up @@ -1497,18 +1501,18 @@ export class AssetInterfaceDescriptionUtil {
modelType: "SubmodelElementCollection",
});

const interfaceMetadata: Record<string, unknown> = {
idShort: "InterfaceMetadata",
const interactionMetadata: Record<string, unknown> = {
idShort: "InteractionMetadata",
semanticId: this.createSemanticId(
"https://admin-shell.io/idta/AssetInterfacesDescription/1/0/InterfaceMetadata"
"https://admin-shell.io/idta/AssetInterfacesDescription/1/0/InteractionMetadata"
),
supplementalSemanticIds: [this.createSemanticId("https://www.w3.org/2019/wot/td#InteractionAffordance")],
// embeddedDataSpecifications ?
value: values,
modelType: "SubmodelElementCollection",
};

return interfaceMetadata;
return interactionMetadata;
}
}

Expand Down
74 changes: 36 additions & 38 deletions packages/td-tools/test/AssetInterfaceDescriptionTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,8 @@ class AssetInterfaceDescriptionUtilTest {
expect(tdObj.properties.device_name.forms[0])
.to.have.property("href")
.to.eql("modbus+tcp://192.168.178.146:502/1/40020?quantity=16");
expect(tdObj.properties.device_name.forms[0])
.to.have.property("modbus:function")
.to.eql("readHoldingRegisters");
expect(tdObj.properties.device_name.forms[0]).to.have.property("modbus:type").to.eql("string");
expect(tdObj.properties.device_name.forms[0]).to.have.property("modv:function").to.eql("readHoldingRegisters");
expect(tdObj.properties.device_name.forms[0]).to.have.property("modv:type").to.eql("string");
expect(tdObj.properties.device_name.forms[0])
.to.have.property("contentType")
.to.eql("application/octet-stream");
Expand Down Expand Up @@ -270,8 +268,8 @@ class AssetInterfaceDescriptionUtilTest {
expect(tdObj.properties.soc.forms[0])
.to.have.property("href")
.to.eql("modbus+tcp://192.168.178.146:502/40361?quantity=1");
expect(tdObj.properties.soc.forms[0]).to.have.property("modbus:function").to.eql("readHoldingRegisters");
expect(tdObj.properties.soc.forms[0]).to.have.property("modbus:type").to.eql("uint16be");
expect(tdObj.properties.soc.forms[0]).to.have.property("modv:function").to.eql("readHoldingRegisters");
expect(tdObj.properties.soc.forms[0]).to.have.property("modv:type").to.eql("uint16be");
expect(tdObj.properties.soc.forms[0]).to.have.property("contentType").to.eql("application/octet-stream");
expect(tdObj.properties.device_name.forms[0]).not.to.have.property("security");
}
Expand Down Expand Up @@ -352,11 +350,11 @@ class AssetInterfaceDescriptionUtilTest {
expect(hasThingTitle, "No thing title").to.equal(true);
expect(hasEndpointMetadata, "No EndpointMetadata").to.equal(true);

// InterfaceMetadata with properties etc
let hasInterfaceMetadata = false;
// InteractionMetadata with properties etc
let hasInteractionMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "InterfaceMetadata") {
hasInterfaceMetadata = true;
if (smValue.idShort === "InteractionMetadata") {
hasInteractionMetadata = 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) {
Expand Down Expand Up @@ -410,12 +408,12 @@ class AssetInterfaceDescriptionUtilTest {
} else if (formEntry.idShort === "contentType") {
hasContentType = true;
expect(formEntry.value).to.equal("application/octet-stream");
} else if (formEntry.idShort === "modbus_function") {
// vs. "modbus:function"
} else if (formEntry.idShort === "modv_function") {
// vs. "modv:function"
hasModbusFunction = true;
expect(formEntry.value).to.equal("readHoldingRegisters");
} else if (formEntry.idShort === "modbus_type") {
// vs. "modbus:type"
} else if (formEntry.idShort === "modv_type") {
// vs. "modv:type"
hasModbusType = true;
expect(formEntry.value).to.equal("string");
}
Expand Down Expand Up @@ -476,12 +474,12 @@ class AssetInterfaceDescriptionUtilTest {
} else if (formEntry.idShort === "contentType") {
hasContentType = true;
expect(formEntry.value).to.equal("application/octet-stream");
} else if (formEntry.idShort === "modbus_function") {
// vs. "modbus:function"
} else if (formEntry.idShort === "modv_function") {
// vs. "modv:function"
hasModbusFunction = true;
expect(formEntry.value).to.equal("readHoldingRegisters");
} else if (formEntry.idShort === "modbus_type") {
// vs. "modbus:type"
} else if (formEntry.idShort === "modv_type") {
// vs. "modv:type"
hasModbusType = true;
expect(formEntry.value).to.equal("uint16be");
}
Expand All @@ -506,7 +504,7 @@ class AssetInterfaceDescriptionUtilTest {
expect(hasProperties).to.equal(true);
}
}
expect(hasInterfaceMetadata, "No InterfaceMetadata").to.equal(true);
expect(hasInteractionMetadata, "No InteractionMetadata").to.equal(true);
}

td1Base = "https://www.example.com/";
Expand Down Expand Up @@ -671,11 +669,11 @@ class AssetInterfaceDescriptionUtilTest {
expect(hasThingTitle, "No thing title").to.equal(true);
expect(hasEndpointMetadata, "No EndpointMetadata").to.equal(true);

// InterfaceMetadata with properties etc
let hasInterfaceMetadata = false;
// InteractionMetadata with properties etc
let hasInteractionMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "InterfaceMetadata") {
hasInterfaceMetadata = true;
if (smValue.idShort === "InteractionMetadata") {
hasInteractionMetadata = 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) {
Expand Down Expand Up @@ -806,7 +804,7 @@ class AssetInterfaceDescriptionUtilTest {
expect(hasProperties).to.equal(true);
}
}
expect(hasInterfaceMetadata, "No InterfaceMetadata").to.equal(true);
expect(hasInteractionMetadata, "No InteractionMetadata").to.equal(true);

// Test to use all possible prefixes -> in this case it is only https
// Note: id is autogenerated (if not present) -> needs to be exluded/removed/set in TD
Expand All @@ -825,11 +823,11 @@ class AssetInterfaceDescriptionUtilTest {
const smInterface = smObj.submodelElements[0];
expect(smInterface).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0);

// InterfaceMetadata with *no* properties for this protocol
let hasInterfaceMetadata = false;
// InteractionMetadata with *no* properties for this protocol
let hasInteractionMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "InterfaceMetadata") {
hasInterfaceMetadata = true;
if (smValue.idShort === "InteractionMetadata") {
hasInteractionMetadata = true;
expect(smValue).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0);
for (const interactionValues of smValue.value) {
if (interactionValues.idShort === "properties") {
Expand All @@ -838,7 +836,7 @@ class AssetInterfaceDescriptionUtilTest {
}
}
}
expect(hasInterfaceMetadata, "No InterfaceMetadata").to.equal(true);
expect(hasInteractionMetadata, "No InteractionMetadata").to.equal(true);
}

@test async "should correctly transform sample TD1 into JSON AAS"() {
Expand Down Expand Up @@ -866,8 +864,8 @@ class AssetInterfaceDescriptionUtilTest {
{
href: "modbus+tcp://127.0.0.1:60000/1",
op: "readproperty",
"modbus:function": "readCoil",
"modbus:pollingTime": 1,
"modv:function": "readCoil",
"modv:pollingTime": 1,
},
],
},
Expand Down Expand Up @@ -941,11 +939,11 @@ class AssetInterfaceDescriptionUtilTest {
expect(hasThingTitle, "No thing title").to.equal(true);
expect(hasEndpointMetadata, "No EndpointMetadata").to.equal(true);

// InterfaceMetadata with properties etc
let hasInterfaceMetadata = false;
// InteractionMetadata with properties etc
let hasInteractionMetadata = false;
for (const smValue of smInterface.value) {
if (smValue.idShort === "InterfaceMetadata") {
hasInterfaceMetadata = true;
if (smValue.idShort === "InteractionMetadata") {
hasInteractionMetadata = 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) {
Expand Down Expand Up @@ -995,10 +993,10 @@ class AssetInterfaceDescriptionUtilTest {
hasOp = true;
// Note: AID does not know "op"
// expect(formEntry.value).to.equal("readproperty");
} else if (formEntry.idShort === "modbus_function") {
} else if (formEntry.idShort === "modv_function") {
hasModbusFunction = true;
expect(formEntry.value).to.equal("readCoil");
} else if (formEntry.idShort === "modbus_pollingTime") {
} else if (formEntry.idShort === "modv_pollingTime") {
hasModbusAddress = true;
expect(formEntry.value).to.equal("1");
expect(formEntry.valueType).to.equal("xs:int");
Expand All @@ -1023,7 +1021,7 @@ class AssetInterfaceDescriptionUtilTest {
expect(hasProperties).to.equal(true);
}
}
expect(hasInterfaceMetadata, "No InterfaceMetadata").to.equal(true);
expect(hasInteractionMetadata, "No InteractionMetadata").to.equal(true);
}

@test.skip async "should correctly transform counter TD into JSON AAS"() {
Expand Down
Loading

0 comments on commit 33be732

Please sign in to comment.