canopen
Version:
CANopen implementation for Javascript
579 lines (495 loc) • 20.8 kB
JavaScript
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const { DataType, AccessType, ObjectType, EdsError, Eds } = require('../index');
const fs = require('fs');
const expect = chai.expect;
chai.use(chaiAsPromised);
describe('Eds', function () {
describe('File IO', function () {
let testFile;
before(function () {
testFile = `test${Date.now()}.EDS`;
if (fs.existsSync(testFile)) {
let base = testFile;
let count = 1;
do {
testFile = base + '.' + count.toString();
count += 1;
}
while (fs.existsSync(testFile));
}
});
after(function () {
// Delete the generated EDS file.
if (fs.existsSync(testFile))
fs.unlinkSync(testFile);
});
it('should save to and load from a file', function () {
const date = new Date(0);
const saveFile = new Eds({
fileName: testFile,
creationDate: date,
baudRates: [500000],
});
saveFile.save(testFile);
const loadFile = Eds.fromFile(testFile);
return Promise.all([
expect(loadFile.fileName).to.equal(testFile),
expect(loadFile.baudRates).to.include(500000),
expect(loadFile.creationDate.getTime())
.to.equal(date.getTime()),
]);
});
it('should properly convert a boolean values', function () {
const eds = new Eds();
expect(eds.lssSupported).to.equal(false);
eds.lssSupported = true;
expect(eds.lssSupported).to.equal(true);
});
});
describe('Add entry', function () {
it('should require parameterName', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
}).to.throw(EdsError);
});
it('should not allow an unknown objectType', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'DataObject',
objectType: -1,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
}).to.throw(EdsError);
});
describe('ObjectType is DEFTYPE or VAR', function () {
it('should require dataType', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'VAR',
objectType: ObjectType.VAR,
accessType: AccessType.READ_WRITE,
});
}).to.throw(EdsError);
});
it('should not allow compactSubObj', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'VAR',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
compactSubObj: true, // Not allowed
}).to.throw(EdsError);
});
});
it('should emit on value update', function (done) {
const eds = new Eds();
const obj = eds.addEntry(0x2000, {
parameterName: 'VAR',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
obj.addListener('update', () => {
done();
});
obj.value = 1;
});
});
describe('ObjectType is DEFSTRUCT, ARRAY, or RECORD', function () {
describe('compactSubObj is false', function () {
it('should not allow dataType', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
dataType: DataType.UNSIGNED8,
}).to.throw(EdsError);
});
});
it('should not allow defaultValue', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
defaultValue: 0,
}).to.throw(EdsError);
});
});
it('should not allow pdoMapping', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
pdoMapping: false,
}).to.throw(EdsError);
});
});
it('should not allow lowLimit', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
lowLimit: null,
}).to.throw(EdsError);
});
});
it('should not allow highLimit', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
highLimit: null,
}).to.throw(EdsError);
});
});
});
describe('CompactSubObj is true', function () {
it('should require dataType', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
accessType: AccessType.READ_WRITE,
compactSubObj: true,
});
}).to.throw(EdsError);
});
});
});
describe('ObjectType is DOMAIN', function () {
it('should not allow pdoMapping', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'ARRAY',
objectType: ObjectType.ARRAY,
pdoMapping: false,
}).to.throw(EdsError);
});
});
it('should not allow lowLimit', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'DOMAIN',
objectType: ObjectType.DOMAIN,
lowLimit: null,
}).to.throw(EdsError);
});
});
it('should not allow highLimit', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'DOMAIN',
objectType: ObjectType.DOMAIN,
highLimit: null,
}).to.throw(EdsError);
});
});
it('should not allow compactSubObj', function () {
const eds = new Eds();
return expect(() => {
eds.addEntry(0x2000, {
parameterName: 'DOMAIN',
objectType: ObjectType.DOMAIN,
compactSubObj: false,
}).to.throw(EdsError);
});
});
});
});
describe('Remove entry', function () {
it('should remove an entry', function () {
const eds = new Eds();
eds.addEntry(0x2000, {
parameterName: 'Test entry',
dataType: DataType.UNSIGNED8,
});
eds.removeEntry(0x2000);
return expect(eds.getEntry(0x2000)).to.equal(undefined);
});
it('should remove exactly one entry from the name lookup', function () {
const eds = new Eds();
eds.addEntry(0x2000, {
parameterName: 'Test entry',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
eds.addEntry(0x2001, {
parameterName: 'Test entry',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
eds.addEntry(0x2002, {
parameterName: 'Test entry',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
expect(eds.findEntry('Test entry').length).to.equal(3);
eds.removeEntry(0x2000);
const entries = eds.findEntry('Test entry');
return Promise.all([
expect(entries.length).to.equal(2),
expect(entries[0].index).to.equal(0x2001),
expect(entries[1].index).to.equal(0x2002),
]);
});
it('should throw if an entry does not exist', function () {
const eds = new Eds();
expect(() => eds.removeEntry(0x2003)).to.throw(EdsError);
});
});
describe('Sub-entries', function () {
it('should create sub-entry 0', function () {
const eds = new Eds();
const entry = eds.addEntry(0x2000, {
parameterName: 'Test entry',
objectType: ObjectType.ARRAY,
});
expect(entry[0]).to.exist;
});
it('should add sub-entires', function () {
const eds = new Eds();
const entry = eds.addEntry(0x2000, {
parameterName: 'Test entry',
objectType: ObjectType.ARRAY,
});
expect(entry.subNumber).to.equal(1);
expect(entry[0].value).to.equal(0);
eds.addSubEntry(0x2000, 1, {
parameterName: 'Sub-entry 1',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
expect(entry.subNumber).to.equal(2);
expect(entry[0].value).to.equal(1);
eds.addSubEntry(0x2000, 10, {
parameterName: 'Sub-entry 10',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
expect(entry.subNumber).to.equal(3);
expect(entry[0].value).to.equal(10);
});
it('should remove sub-entries', function () {
const eds = new Eds();
const entry = eds.addEntry(0x2000, {
parameterName: 'Test entry',
objectType: ObjectType.ARRAY,
});
eds.addSubEntry(0x2000, 1, {
parameterName: 'Sub-entry 1',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
eds.addSubEntry(0x2000, 10, {
parameterName: 'Sub-entry 10',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
});
eds.removeSubEntry(0x2000, 10);
expect(entry.subNumber).to.equal(2);
expect(entry[0].value).to.equal(1);
eds.removeSubEntry(0x2000, 1);
expect(entry.subNumber).to.equal(1);
expect(entry[0].value).to.equal(0);
});
it('should not remove sub-entry 0', function () {
const eds = new Eds();
expect(() => eds.removeSubEntry(0x2000, 0)).to.throw(EdsError);
});
});
describe('Scaling', function () {
it('should scale numeric values', function () {
const eds = new Eds();
const obj = eds.addEntry(0x2000, {
parameterName: 'VAR',
objectType: ObjectType.VAR,
dataType: DataType.UNSIGNED8,
accessType: AccessType.READ_WRITE,
defaultValue: 2,
});
// 2 * 10 = 20
obj.scaleFactor = 10;
expect(obj.value).to.equal(20);
// 2 * 0.5 = 1
obj.scaleFactor = 0.5;
expect(obj.value).to.equal(1);
// 5 / 0.5 = 10
obj.value = 5;
obj.scaleFactor = 1;
expect(obj.value).to.equal(10);
});
});
describe('Accessors', function() {
it('should get 0x1002', function () {
const eds = new Eds();
eds.setStatusRegister(100);
expect(eds.getStatusRegister()).to.equal(100);
});
it('should get 0x1001', function () {
const eds = new Eds();
eds.setErrorRegister({ generic: true });
expect(eds.getErrorRegister()).to.equal(1);
eds.setErrorRegister({ current: true });
expect(eds.getErrorRegister()).to.equal(3);
eds.setErrorRegister({ voltage: true });
expect(eds.getErrorRegister()).to.equal(7);
eds.setErrorRegister({ temperature: true });
expect(eds.getErrorRegister()).to.equal(15);
eds.setErrorRegister({ generic: false, communication: true });
expect(eds.getErrorRegister()).to.equal(30);
eds.setErrorRegister({ current: false, device: true });
expect(eds.getErrorRegister()).to.equal(60);
eds.setErrorRegister({ voltage: false, manufacturer: true });
expect(eds.getErrorRegister()).to.equal(184);
eds.setErrorRegister({ temperature: false });
expect(eds.getErrorRegister()).to.equal(176);
});
it('should get 0x1003', function () {
const eds = new Eds();
eds.setErrorHistoryLength(2);
eds.pushErrorHistory(0x1000);
expect(eds.getErrorHistory()[0].code).to.equal(0x1000);
expect(eds.getErrorHistory()[0].info).to.equal(0);
eds.pushErrorHistory(0x2000, 'CO');
expect(eds.getErrorHistory()[0].code).to.equal(0x2000);
expect(eds.getErrorHistory()[0].info).to.equal(0x4f43);
expect(eds.getErrorHistory()[1].code).to.equal(0x1000);
expect(eds.getErrorHistory()[1].info).to.equal(0);
eds.pushErrorHistory(0x3000, 7);
expect(eds.getErrorHistory()[0].code).to.equal(0x3000);
expect(eds.getErrorHistory()[0].info).to.equal(7);
expect(eds.getErrorHistory()[1].code).to.equal(0x2000);
expect(eds.getErrorHistory()[1].info).to.equal(0x4f43);
});
it('should get 0x1005', function () {
const eds = new Eds();
expect(eds.getSyncCobId()).to.be.null;
expect(eds.getSyncGenerationEnable()).to.be.false;
eds.setSyncCobId(0x80);
eds.setSyncGenerationEnable(true);
expect(eds.getSyncCobId()).to.equal(0x80);
expect(eds.getSyncGenerationEnable()).to.be.true;
});
it('should get 0x1006', function () {
const eds = new Eds();
expect(eds.getSyncCyclePeriod()).to.be.null;
eds.setSyncCyclePeriod(333);
expect(eds.getSyncCyclePeriod()).to.equal(333);
});
it('should get 0x1008', function () {
const eds = new Eds();
eds.setDeviceName('node-canopen');
expect(eds.getDeviceName()).to.equal('node-canopen');
});
it('should get 0x1009', function () {
const eds = new Eds();
eds.setHardwareVersion('A');
expect(eds.getHardwareVersion()).to.equal('A');
});
it('should get 0x1019', function () {
const eds = new Eds();
expect(eds.getSyncOverflow()).to.be.null;
eds.setSyncOverflow(10);
expect(eds.getSyncOverflow()).to.equal(10);
});
it('should get 0x1012', function () {
const eds = new Eds();
expect(eds.getTimeCobId()).to.be.null;
expect(eds.getTimeProducerEnable()).to.be.false;
expect(eds.getTimeConsumerEnable()).to.be.false;
eds.setTimeCobId(0x100);
expect(eds.getTimeCobId()).to.equal(0x100);
eds.setTimeProducerEnable(true);
expect(eds.getTimeProducerEnable()).to.be.true;
expect(eds.getTimeConsumerEnable()).to.be.false;
eds.setTimeProducerEnable(false);
eds.setTimeConsumerEnable(true);
expect(eds.getTimeProducerEnable()).to.be.false;
expect(eds.getTimeConsumerEnable()).to.be.true;
eds.setTimeConsumerEnable(false);
expect(eds.getTimeProducerEnable()).to.be.false;
expect(eds.getTimeConsumerEnable()).to.be.false;
});
it('should get 0x100A', function () {
const eds = new Eds();
eds.setSoftwareVersion('1.0');
expect(eds.getSoftwareVersion()).to.equal('1.0');
});
it('should get 0x1014', function () {
const eds = new Eds();
expect(eds.getEmcyCobId()).to.be.null;
expect(eds.getEmcyValid()).to.be.false;
eds.setEmcyCobId(0x8A);
expect(eds.getEmcyCobId()).to.equal(0x8A);
expect(eds.getEmcyValid()).to.be.true;
});
it('should get 0x1015', function () {
const eds = new Eds();
expect(eds.getEmcyInhibitTime()).to.be.null;
eds.setEmcyInhibitTime(100);
expect(eds.getEmcyInhibitTime()).to.equal(100);
});
it('should get 0x1016', function () {
const eds = new Eds();
eds.addHeartbeatConsumer(0xB, 100);
const consumers = eds.getHeartbeatConsumers();
expect(consumers[0].deviceId).to.equal(0xB);
expect(consumers[0].heartbeatTime).to.equal(100);
eds.removeHeartbeatConsumer(0xB);
expect(eds.getHeartbeatConsumers()).to.be.an('array').that.is.empty;
});
it('should get 0x1017', function () {
const eds = new Eds();
expect(eds.getHeartbeatProducerTime()).to.be.null;
eds.setHeartbeatProducerTime(500);
expect(eds.getHeartbeatProducerTime()).to.equal(500);
});
it('should get 0x1018', function () {
const eds = new Eds();
eds.setIdentity({
vendorId: 1,
productCode: 2,
revisionNumber: 3,
serialNumber: 4,
});
const identity = eds.getIdentity();
expect(identity.vendorId).to.equal(1);
expect(identity.productCode).to.equal(2);
expect(identity.revisionNumber).to.equal(3);
expect(identity.serialNumber).to.equal(4);
});
it('should get 0x1028', function () {
const eds = new Eds();
eds.addEmcyConsumer(0x3);
expect(eds.getEmcyConsumers()[0]).to.equal(0x3);
eds.removeEmcyConsumer(0x3);
expect(eds.getEmcyConsumers()).to.be.an('array').that.is.empty;
});
});
});