Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions src/objects/device/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -696,13 +696,10 @@ export class BDDevice extends BDObject implements AsyncEventEmitter<BDDeviceEven
this.#wrapReqHandler(req, async () => {
const { header, invokeId, payload: { properties } } = req;
if (!header) return;
const values: BACNetReadAccess[] = [];
for (const { objectId, properties: objProperties } of properties) {
const object = this.#objects.get(getObjectUID(objectId));
if (object) {
values.push(await object.___readPropertyMultiple(objProperties));
}
}
const values: BACNetReadAccess[] = await Promise.all(properties.map(({ objectId, properties: objProperties }) => {
const object = this.#getObjectByIdOrThrow(objectId);
return object.___readPropertyMultiple(objProperties);
}));
this.#client.readPropertyMultipleResponse(header.sender, invokeId!, values);
});
};
Expand Down
17 changes: 7 additions & 10 deletions src/objects/generic/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,16 +320,13 @@ export class BDObject extends AsyncEventEmitter<BDObjectEvents> {
return this.___readPropertyMultipleAll();
}
const ctx: BDPropertyAccessContext = { date: new Date() };
const values: BACNetReadAccess['values'] = [];
for (const identifier of identifiers) {
const property = this.#properties.get(identifier.id);
if (property) {
values.push({
property: identifier,
value: ensureArray(property.___readData(identifier.index, ctx))
});
}
}
const values: BACNetReadAccess['values'] = await Promise.all(identifiers.map((identifier) => {
const property = this.___getPropertyOrThrow(identifier.id);
return {
property: identifier,
value: ensureArray(property.___readData(identifier.index, ctx))
};
}));
return { objectId: this.identifier.value, values };
}

Expand Down
20 changes: 19 additions & 1 deletion src/tests/bacnet-stack-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ export const bsReadProperty = async (devIn: number, objType: ObjectType, objIn:
return await bsExec('bacrp', args);
};

export const bsReadMultiple = async (devIn: number, tuples: [objType: ObjectType, objIn: number, propId: PropertyIdentifier, index?: number][]) => {
// bacrpm 123 analog-input 77 85 analog-input 78 85
const args = tuples.flatMap((tuple) => {
const ser = [`${tuple[0]}`, `${tuple[1]}`];
if (tuple[3] !== undefined) {
ser.push(`${tuple[2]}[${tuple[3]}]`);
} else {
ser.push(`${tuple[2]}`);
}
return ser;
});
args.unshift(`${devIn}`);
const res = await bsExec('bacrpm', args)
return res.replaceAll(/\r?\n/g, ' ')
.replaceAll(/\s+/g, ' ')
.trim();
};

export const bsWriteProperty = async (
devIn: number,
objType: ObjectType,
Expand All @@ -41,4 +59,4 @@ export const bsWriteProperty = async (
`${tag}`,
`${value}`,
]);
};
};
62 changes: 62 additions & 0 deletions src/tests/read-multiple.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { it, describe, beforeEach, afterEach } from 'node:test';
import { deepStrictEqual } from 'node:assert';
import { BDDevice } from '../objects/device/device.js';
import { bsReadMultiple, bsReadProperty } from './bacnet-stack-client.js';
import { BDDateTimeValue } from '../objects/temporal/datetimevalue.js';
import { EngineeringUnits, ObjectType, PropertyIdentifier } from '@bacnet-js/client';
import { BDAnalogValue } from '../objects/numeric/analogvalue.js';

describe('ReadMultiple', () => {

let device: BDDevice;
let av_1: BDAnalogValue;
let av_2: BDAnalogValue;

beforeEach(async () => {
device = new BDDevice(1, {
name: 'Test Device',
});
device.on('error', console.error);
av_1 = device.addObject(new BDAnalogValue({
name: 'Test AV 1',
description: 'A test analog value',
presentValue: 10,
unit: EngineeringUnits.PERCENT,
}));
av_2 = device.addObject(new BDAnalogValue({
name: 'Test AV 2',
description: 'A test analog value',
presentValue: 90,
unit: EngineeringUnits.PERCENT,
}));
});

afterEach(async () => {
device.destroy();
});

it('should read multiple Present_Value properties across different objects', async () => {
const res = await bsReadMultiple(device.identifier.value.instance, [
[ObjectType.ANALOG_VALUE, av_1.identifier.value.instance, PropertyIdentifier.PRESENT_VALUE],
[ObjectType.ANALOG_VALUE, av_2.identifier.value.instance, PropertyIdentifier.PRESENT_VALUE],
]);
deepStrictEqual(res, `analog-value #1 { present-value: 10.000000 } analog-value #2 { present-value: 90.000000 }`);
});

it('should fail to read multiple Present_Value properties across a mix of existent and non-existent properties', async () => {
const res = await bsReadMultiple(device.identifier.value.instance, [
[ObjectType.ANALOG_VALUE, av_1.identifier.value.instance, PropertyIdentifier.PRESENT_VALUE],
[ObjectType.ANALOG_VALUE, 42, PropertyIdentifier.PRESENT_VALUE],
]);
deepStrictEqual(res, ``);
});

it('should fail to read a mix of existent and non-existent properties on the same object', async () => {
const res = await bsReadMultiple(device.identifier.value.instance, [
[ObjectType.ANALOG_VALUE, av_1.identifier.value.instance, PropertyIdentifier.PRESENT_VALUE],
[ObjectType.ANALOG_VALUE, av_1.identifier.value.instance, PropertyIdentifier.ACCESS_EVENT_TAG],
]);
deepStrictEqual(res, ``);
});

});
Loading