Correct pathing
This commit is contained in:
@@ -161,7 +161,7 @@ export default class HTTPInterfaceServer extends AbstractInterfaceServer<Config>
|
|||||||
|
|
||||||
// Main function
|
// Main function
|
||||||
this.app.use("/aas/submodels/:smId/submodel-elements/*", (req, res) => {
|
this.app.use("/aas/submodels/:smId/submodel-elements/*", (req, res) => {
|
||||||
const idShortPathString = (req.params as any)[0];
|
const idShortPathString = (req.params as any)[0] as string;
|
||||||
if (idShortPathString.endsWith("/")) res.status(400).end();
|
if (idShortPathString.endsWith("/")) res.status(400).end();
|
||||||
|
|
||||||
const sm = getSM(req.params.smId);
|
const sm = getSM(req.params.smId);
|
||||||
@@ -185,18 +185,20 @@ export default class HTTPInterfaceServer extends AbstractInterfaceServer<Config>
|
|||||||
const endingMatch = endingMatchSearch?.[0];
|
const endingMatch = endingMatchSearch?.[0];
|
||||||
|
|
||||||
const idShortPath = endingMatch ? idShortPathString.replace(new RegExp(`${endingMatch}$`), "").split("/") : idShortPathString.split("/");
|
const idShortPath = endingMatch ? idShortPathString.replace(new RegExp(`${endingMatch}$`), "").split("/") : idShortPathString.split("/");
|
||||||
|
// We test both options of path, the correct one (map.entry.list[0].property) and the BaSyx one (map/entry/list/listElement/property)
|
||||||
|
const elem = idShortPath.length <= 1 ? Traverser.getElementByPath(this.aas, sm, idShortPath[0]) : Traverser.getElementByIdPath(this.aas, sm, idShortPath);
|
||||||
|
|
||||||
switch (endingMatch) {
|
switch (endingMatch) {
|
||||||
case undefined: {
|
case undefined: {
|
||||||
if (req.method !== "GET") return res.status(501).end();
|
if (req.method !== "GET") return res.status(501).end();
|
||||||
const prop = Traverser.getElementByIdPath(this.aas, sm, idShortPath);
|
const prop = elem;
|
||||||
if (prop === null || !AASCoreTypes.isProperty(prop)) return res.status(404).end();
|
if (prop === null || !AASCoreTypes.isProperty(prop)) return res.status(404).end();
|
||||||
|
|
||||||
return res.json(AASCoreJsonization.toJsonable(prop)).end();
|
return res.json(AASCoreJsonization.toJsonable(prop)).end();
|
||||||
}
|
}
|
||||||
case "/value":
|
case "/value": {
|
||||||
if (req.method !== "GET" && req.method !== "PUT") return res.status(501).end();
|
if (req.method !== "GET" && req.method !== "PUT") return res.status(501).end();
|
||||||
const prop = Traverser.getElementByIdPath(this.aas, sm, idShortPath);
|
const prop = elem;
|
||||||
if (prop === null || !AASCoreTypes.isProperty(prop)) return res.status(404).end();
|
if (prop === null || !AASCoreTypes.isProperty(prop)) return res.status(404).end();
|
||||||
|
|
||||||
const request: Types.RequestTypes.Request = req.method === "GET" ? { type: "readProperty", target: prop } : { type: "writeProperty", target: prop, extraData: { value: req.body } };
|
const request: Types.RequestTypes.Request = req.method === "GET" ? { type: "readProperty", target: prop } : { type: "writeProperty", target: prop, extraData: { value: req.body } };
|
||||||
@@ -207,11 +209,12 @@ export default class HTTPInterfaceServer extends AbstractInterfaceServer<Config>
|
|||||||
|
|
||||||
if (value === null) return res.status(500).end();
|
if (value === null) return res.status(500).end();
|
||||||
return res.json(value).end();
|
return res.json(value).end();
|
||||||
|
}
|
||||||
case "/attachment":
|
case "/attachment":
|
||||||
return res.status(501).end();
|
return res.status(501).end();
|
||||||
case "/invoke": {
|
case "/invoke": {
|
||||||
if (req.method !== "POST") return res.status(405).end();
|
if (req.method !== "POST") return res.status(405).end();
|
||||||
const op = Traverser.getElementByIdPath(this.aas, sm, idShortPath);
|
const op = elem;
|
||||||
if (op === null || !AASCoreTypes.isOperation(op)) return res.status(404).end();
|
if (op === null || !AASCoreTypes.isOperation(op)) return res.status(404).end();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -223,7 +226,7 @@ export default class HTTPInterfaceServer extends AbstractInterfaceServer<Config>
|
|||||||
}
|
}
|
||||||
case "/invoke-async": {
|
case "/invoke-async": {
|
||||||
if (req.method !== "POST") return res.status(405).end();
|
if (req.method !== "POST") return res.status(405).end();
|
||||||
const op = Traverser.getElementByIdPath(this.aas, sm, idShortPath);
|
const op = elem;
|
||||||
if (op === null || !AASCoreTypes.isOperation(op)) return res.status(404).end();
|
if (op === null || !AASCoreTypes.isOperation(op)) return res.status(404).end();
|
||||||
|
|
||||||
const request: Types.RequestTypes.Request = { type: "invokeAction", target: op, extraData: { args: req.body, async: true } };
|
const request: Types.RequestTypes.Request = { type: "invokeAction", target: op, extraData: { args: req.body, async: true } };
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export default class MQTTConnector extends AbstractConnectionObject<mqtt.IClient
|
|||||||
public callActionSync(_target: AASCoreTypes.Operation, mapping: Types.RequestTypes.ConnectionConfiguration, args: Record<string, any>): any {
|
public callActionSync(_target: AASCoreTypes.Operation, mapping: Types.RequestTypes.ConnectionConfiguration, args: Record<string, any>): any {
|
||||||
this.assertConnected();
|
this.assertConnected();
|
||||||
|
|
||||||
const form: Types.AIDTypes.InterfaceFormMQTTAction = mapping.forms[0];
|
const form: Types.AIDTypes.InterfaceFormMQTTAction = mapping.forms[0] as Types.AIDTypes.InterfaceFormMQTTAction;
|
||||||
const topic = MQTTConnector.getTopicFromForm(form);
|
const topic = MQTTConnector.getTopicFromForm(form);
|
||||||
if (topic === undefined || !mapping.forms) throw new Error("Mapping invalid.");
|
if (topic === undefined || !mapping.forms) throw new Error("Mapping invalid.");
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ export default class MQTTConnector extends AbstractConnectionObject<mqtt.IClient
|
|||||||
public callActionAsync(_target: AASCoreTypes.Operation, mapping: Types.RequestTypes.ConnectionConfiguration, args: Record<string, any>): string {
|
public callActionAsync(_target: AASCoreTypes.Operation, mapping: Types.RequestTypes.ConnectionConfiguration, args: Record<string, any>): string {
|
||||||
this.assertConnected();
|
this.assertConnected();
|
||||||
|
|
||||||
const form: Types.AIDTypes.InterfaceFormMQTTAction = mapping.forms[0];
|
const form: Types.AIDTypes.InterfaceFormMQTTAction = mapping.forms[0] as Types.AIDTypes.InterfaceFormMQTTAction;
|
||||||
const topic = MQTTConnector.getTopicFromForm(form);
|
const topic = MQTTConnector.getTopicFromForm(form);
|
||||||
if (topic === undefined || !mapping.forms) throw new Error("Mapping invalid.");
|
if (topic === undefined || !mapping.forms) throw new Error("Mapping invalid.");
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,39 @@ export default class Traverser {
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverse donwards from a start element by using a path.
|
||||||
|
* @param start Start element
|
||||||
|
* @param path a JS-linke path, eg. map.entry.list[0].property
|
||||||
|
* @returns Target element at the end of the path or null if not found
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
public static traverseByPath(start: types.Class, path: string): types.Class | null {
|
||||||
|
const ids = path.split(".");
|
||||||
|
let current: types.Class | null = start;
|
||||||
|
const regex = /(\[\d+\])+$/i
|
||||||
|
|
||||||
|
for (const idShort of ids) {
|
||||||
|
if (!idShort) continue;
|
||||||
|
if (current === null) return null;
|
||||||
|
|
||||||
|
current = Traverser.findChildByIdShort(current, idShort);
|
||||||
|
const match = regex.exec(idShort)?.[0];
|
||||||
|
if (match) {
|
||||||
|
const indexes = match.split("][").map(index => parseInt(index.replace("[", "").replace("]", "")));
|
||||||
|
|
||||||
|
for (const index of indexes) {
|
||||||
|
if (current === null || !types.isSubmodelElementList(current) || current.value === null) return null;
|
||||||
|
const next = current.value?.at(index);
|
||||||
|
if (next === undefined) return null;
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get element by using idShorts from an submodel.
|
* Get element by using idShorts from an submodel.
|
||||||
* @param env Environment to search in
|
* @param env Environment to search in
|
||||||
@@ -124,6 +157,20 @@ export default class Traverser {
|
|||||||
return Traverser.traverseByShortIds(sm, idShorts);
|
return Traverser.traverseByShortIds(sm, idShorts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get element by using a path from an submodel.
|
||||||
|
* @param env Environment to search in
|
||||||
|
* @param submodelOrIdShort Submodel element or Submodel idShort
|
||||||
|
* @param path path to traverse by
|
||||||
|
* @returns Target element at the end of the path or null if not found
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
public static getElementByPath(env: types.Environment, submodelOrIdShort: string | types.Submodel, path: string): types.Class | null {
|
||||||
|
const sm = typeof submodelOrIdShort === "string" ? Traverser.findSMByIdShort(env, submodelOrIdShort) : submodelOrIdShort;
|
||||||
|
if (sm === null) return null;
|
||||||
|
return Traverser.traverseByPath(sm, path);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find an element using a function to check.
|
* Find an element using a function to check.
|
||||||
* @param start Start element
|
* @param start Start element
|
||||||
|
|||||||
Reference in New Issue
Block a user