Skip to content

Commit

Permalink
Merge pull request #993 from JKRhb/refactor-coap-server
Browse files Browse the repository at this point in the history
refactor(binding-coap): refactor server's expose method
  • Loading branch information
relu91 authored May 25, 2023
2 parents 0477818 + d3fb9d4 commit badc223
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 65 deletions.
190 changes: 126 additions & 64 deletions packages/binding-coap/src/coap-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,81 +136,143 @@ export default class CoapServer implements ProtocolServer {
}
}

public expose(thing: ExposedThing, tdTemplate?: WoT.ExposedThingInit): Promise<void> {
let urlPath = slugify(thing.title, { lower: true });
public async expose(thing: ExposedThing, tdTemplate?: WoT.ExposedThingInit): Promise<void> {
const port = this.getPort();
const urlPath = this.createThingUrlPath(thing);

if (port === -1) {
warn("CoapServer is assigned an invalid port, aborting expose process.");
return;
}

this.fillInBindingData(thing, port, urlPath);

debug(`CoapServer on port ${port} exposes '${thing.title}' as unique '/${urlPath}'`);

this.setUpIntroductionMethods(thing, urlPath, port);
}

private createThingUrlPath(thing: ExposedThing) {
const urlPath = slugify(thing.title, { lower: true });

if (this.things.has(urlPath)) {
urlPath = Helpers.generateUniqueName(urlPath);
return Helpers.generateUniqueName(urlPath);
}
this.coreResources.set(urlPath, { urlPath, parameters: thingDescriptionParameters });

debug(`CoapServer on port ${this.getPort()} exposes '${thing.title}' as unique '/${urlPath}'`);
return urlPath;
}

const port = this.getPort();
if (port !== -1) {
this.things.set(urlPath, thing);

// fill in binding data
for (const address of Helpers.getAddresses()) {
for (const type of ContentSerdes.get().getOfferedMediaTypes()) {
const base: string =
this.scheme + "://" + address + ":" + this.getPort() + "/" + encodeURIComponent(urlPath);

for (const propertyName in thing.properties) {
const href = base + "/" + this.PROPERTY_DIR + "/" + encodeURIComponent(propertyName);
const form = new TD.Form(href, type);
ProtocolHelpers.updatePropertyFormWithTemplate(form, thing.properties[propertyName]);
if (thing.properties[propertyName].readOnly) {
form.op = ["readproperty"];
} else if (thing.properties[propertyName].writeOnly) {
form.op = ["writeproperty"];
} else {
form.op = ["readproperty", "writeproperty"];
}
if (thing.properties[propertyName].observable) {
if (!form.op) {
form.op = [];
}
form.op.push("observeproperty");
form.op.push("unobserveproperty");
}
private fillInBindingData(thing: ExposedThing, port: number, urlPath: string) {
const addresses = Helpers.getAddresses();
const offeredMediaTypes = ContentSerdes.get().getOfferedMediaTypes();

thing.properties[propertyName].forms.push(form);
debug(`CoapServer on port ${this.getPort()} assigns '${href}' to Property '${propertyName}'`);
}
for (const address of addresses) {
for (const offeredMediaType of offeredMediaTypes) {
const base = this.createThingBase(address, port, urlPath);

for (const actionName in thing.actions) {
const href = base + "/" + this.ACTION_DIR + "/" + encodeURIComponent(actionName);
const form = new TD.Form(href, type);
ProtocolHelpers.updateActionFormWithTemplate(form, thing.actions[actionName]);
form.op = "invokeaction";
thing.actions[actionName].forms.push(form);
debug(`CoapServer on port ${this.getPort()} assigns '${href}' to Action '${actionName}'`);
}
this.fillInPropertyBindingData(thing, base, port, offeredMediaType);
this.fillInActionBindingData(thing, base, port, offeredMediaType);
this.fillInEventBindingData(thing, base, port, offeredMediaType);
}
}
}

for (const eventName in thing.events) {
const href = base + "/" + this.EVENT_DIR + "/" + encodeURIComponent(eventName);
const form = new TD.Form(href, type);
ProtocolHelpers.updateEventFormWithTemplate(form, thing.events[eventName]);
form.op = ["subscribeevent", "unsubscribeevent"];
thing.events[eventName].forms.push(form);
debug(`CoapServer on port ${this.getPort()} assigns '${href}' to Event '${eventName}'`);
}
} // media types
} // addresses
private createThingBase(address: string, port: number, urlPath: string): string {
return `${this.scheme}://${address}:${port}/${encodeURIComponent(urlPath)}`;
}

const parameters = {
urlPath,
port,
serviceName: "_wot._udp.local",
};
private fillInPropertyBindingData(thing: ExposedThing, base: string, port: number, offeredMediaType: string) {
for (const [propertyName, property] of Object.entries(thing.properties)) {
const opValues = ProtocolHelpers.getPropertyOpValues(property);
const [href, form] = this.createHrefAndForm(
base,
this.PROPERTY_DIR,
propertyName,
offeredMediaType,
opValues
);

this.mdnsIntroducer?.registerExposedThing(thing, parameters);
} // running
ProtocolHelpers.updatePropertyFormWithTemplate(form, property);

return new Promise<void>((resolve, reject) => {
resolve();
});
property.forms.push(form);
this.logHrefAssignment(port, href, "Property", propertyName);
}
}

private fillInActionBindingData(thing: ExposedThing, base: string, port: number, offeredMediaType: string) {
for (const [actionName, action] of Object.entries(thing.actions)) {
const [href, form] = this.createHrefAndForm(
base,
this.ACTION_DIR,
actionName,
offeredMediaType,
"invokeaction"
);

ProtocolHelpers.updateActionFormWithTemplate(form, action);
action.forms.push(form);

this.logHrefAssignment(port, href, "Action", actionName);
}
}

private fillInEventBindingData(thing: ExposedThing, base: string, port: number, offeredMediaType: string) {
for (const [eventName, event] of Object.entries(thing.events)) {
const [href, form] = this.createHrefAndForm(base, this.EVENT_DIR, eventName, offeredMediaType, [
"subscribeevent",
"unsubscribeevent",
]);

ProtocolHelpers.updateEventFormWithTemplate(form, event);
event.forms.push(form);

this.logHrefAssignment(port, href, "Event", eventName);
}
}

private createHrefAndForm(
base: string,
affordancePathSegment: string,
affordanceName: string,
offeredMediaType: string,
opValues: string | string[]
): [string, TD.Form] {
const href = this.createFormHref(base, affordancePathSegment, affordanceName);
const form = this.createAffordanceForm(href, offeredMediaType, opValues);

return [href, form];
}

private createFormHref(base: string, affordancePathSegment: string, affordanceName: string) {
return `${base}/${affordancePathSegment}/${encodeURIComponent(affordanceName)}`;
}

private createAffordanceForm(href: string, offeredMediaType: string, op: string[] | string) {
const form = new TD.Form(href, offeredMediaType);
form.op = op;

return form;
}

private logHrefAssignment(port: number, href: string, affordanceType: string, affordanceName: string) {
debug(`CoapServer on port ${port} assigns '${href}' to ${affordanceType} '${affordanceName}'`);
}

private setUpIntroductionMethods(thing: ExposedThing, urlPath: string, port: number) {
this.createCoreResource(urlPath);
this.things.set(urlPath, thing);

const parameters = {
urlPath,
port,
serviceName: "_wot._udp.local",
};

this.mdnsIntroducer?.registerExposedThing(thing, parameters);
}

private createCoreResource(urlPath: string): void {
this.coreResources.set(urlPath, { urlPath, parameters: thingDescriptionParameters });
}

public async destroy(thingId: string): Promise<boolean> {
Expand Down
25 changes: 24 additions & 1 deletion packages/core/src/protocol-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ReadableStream as PolyfillStream } from "web-streams-polyfill/ponyfill/
import { ActionElement, EventElement, PropertyElement } from "wot-thing-description-types";
import { createLoggers } from "./logger";

const { debug } = createLoggers("core", "protocol-helpers");
const { debug, warn } = createLoggers("core", "protocol-helpers");

export interface IManagedStream {
nodeStream: Readable;
Expand Down Expand Up @@ -380,4 +380,27 @@ export default class ProtocolHelpers {
// No suitable form found for this operation
return finalFormIndex;
}

public static getPropertyOpValues(property: PropertyElement): string[] {
const op: string[] = [];

if (!property.readOnly) {
op.push("writeproperty");
}

if (!property.writeOnly) {
op.push("readproperty");
}

if (op.length === 0) {
warn("Property was declared both as readOnly and writeOnly.");
}

if (property.observable) {
op.push("observeproperty");
op.push("unobserveproperty");
}

return op;
}
}

0 comments on commit badc223

Please sign in to comment.