diff --git a/src/controller/controller.spec.js b/src/controller/controller.spec.js index f7fde4e..c5a46b8 100644 --- a/src/controller/controller.spec.js +++ b/src/controller/controller.spec.js @@ -27,6 +27,39 @@ describe("Controller Class", () => { }).toThrow(); }); + it("Scan Min Overhead Percent", () => { + const plc = new Controller(); + expect(plc.scan_min_overhead_percent).toBe(20); + + plc.scan_min_overhead_percent = 40; + expect(plc.scan_min_overhead_percent).not.toBe(20); + expect(plc.scan_min_overhead_percent).toBe(40); + + expect(() => { + plc.scan_min_overhead_percent = null; + }).toThrow(); + + expect(() => { + plc.scan_min_overhead_percent = undefined; + }).toThrow(); + + expect(() => { + plc.scan_min_overhead_percent = "hello"; + }).toThrow(); + }); + + it("Scan Read Only", () => { + const plc = new Controller(); + expect(plc.scan_read_only).toBeFalsy(); + + plc.scan_read_only = true; + expect(plc.scan_read_only).toBeTruthy(); + + + plc.scan_read_only = false; + expect(plc.scan_read_only).toBeFalsy(); + }); + it("Scanning", () => { const plc = new Controller(); expect(plc.scanning).toBeFalsy(); diff --git a/src/controller/index.js b/src/controller/index.js index 60bde1a..6d25a2a 100644 --- a/src/controller/index.js +++ b/src/controller/index.js @@ -33,7 +33,9 @@ class Controller extends ENIP { }, subs: new TagGroup(compare), scanning: false, - scan_rate: 200 //ms + scan_rate: 200, //ms + scan_min_overhead_percent: 0.2, + scan_read_only: false }; this.workers = { @@ -64,6 +66,47 @@ class Controller extends ENIP { this.state.scan_rate = Math.trunc(rate); } + /** + * Returns the Scan Min Overhead Percent for the Scan method + * + * @memberof Controller + * @returns {number} percent + */ + get scan_min_overhead_percent() { + return this.state.scan_min_overhead_percent * 100; + } + + /** + * Sets the Scan Min Overhead Percent for the Scan method + * + * @memberof Controller + */ + set scan_min_overhead_percent(percent) { + if (typeof percent !== "number") throw new Error("scan_min_overhead_percent must be of Type "); + if (percent < 1 | percent > 99) throw new Error("scan_min_overhead_percent must be >=1 and <=99 "); + this.state.scan_min_overhead_percent = percent / 100; + } + + /** + * Returns the status of the Scan Read Only property + * + * @memberof Controller + * @returns {boolean} percent + */ + get scan_read_only() { + return this.state.scan_read_only; + } + + /** + * Sets the status of the Scan Read Only property + * + * @memberof Controller + */ + set scan_read_only(readOnly) { + if (typeof readOnly !== "boolean") throw new Error("scan_min_overhead_percent must be of Type "); + this.state.scan_read_only = readOnly; + } + /** * Get the status of Scan Group * @@ -396,15 +439,42 @@ class Controller extends ENIP { this.state.subs.add(tag); } + /** + * Removes Tag from Subscription Group + * + * @param {Tagany} tag + * @memberof Controller + */ + unsubscribe(tag) { + this.state.subs.remove(tag); + } + + /** + * Queries Tag in Subscription Group + * + * @param {Tagany} tag + * @memberof Controller + */ + isSubscribed(tag) { + return this.state.subs.contains(tag); + } + /** * Begin Scanning Subscription Group * * @memberof Controller */ - async scan() { + async scan(readOnly = false) { this.state.scanning = true; + this.state.scan_read_only = readOnly; + + let startTime = 0; + let elapsedTime = 0; + while (this.state.scanning) { + startTime = Date.now(); + await this.workers.group .schedule(this._readTagGroup.bind(this), [this.state.subs], { priority: 10, @@ -417,21 +487,24 @@ class Controller extends ENIP { throw e; } }); - - await this.workers.group - .schedule(this._writeTagGroup.bind(this), [this.state.subs], { - priority: 10, - timestamp: new Date() - }) - .catch(e => { - if (e.message) { - throw new Error(`\n ${e.message}`); - } else { - throw e; - } - }); - - await delay(this.state.scan_rate); + + if(!this.state.scan_read_only){ + await this.workers.group + .schedule(this._writeTagGroup.bind(this), [this.state.subs], { + priority: 10, + timestamp: new Date() + }) + .catch(e => { + if (e.message) { + throw new Error(`\n ${e.message}`); + } else { + throw e; + } + }); + } + + elapsedTime = Date.now() - startTime; + await delay(Math.max(this.state.scan_rate - elapsedTime, elapsedTime / (1 - this.state.scan_min_overhead_percent) * (this.state.scan_min_overhead_percent))); } } diff --git a/src/tag-group/index.js b/src/tag-group/index.js index afdb640..e20bead 100644 --- a/src/tag-group/index.js +++ b/src/tag-group/index.js @@ -52,6 +52,17 @@ class TagGroup extends EventEmitter { if (this.state.tags[tag.instance_id]) delete this.state.tags[tag.instance_id]; } + /** + * Queries Tag Group for Tag + * + * @param {Tag} tag - Tag to be Queried in Group + * @memberof TagGroup + */ + contains(tag) { + if(this.state.tags[tag.instance_id]) return true; + return false; + } + /** * Iterable, Allows user to Iterate of each Tag in Group * diff --git a/src/tag-group/tag-group.spec.js b/src/tag-group/tag-group.spec.js index f19a643..4aba2fe 100644 --- a/src/tag-group/tag-group.spec.js +++ b/src/tag-group/tag-group.spec.js @@ -42,4 +42,22 @@ describe("Tag Class", () => { expect(group.generateWriteMessageRequests()).toMatchSnapshot(); }); }); + + describe("Add, Remove, Query Tag Methods", () => { + it("Adds, Removes, Queries Tags", () => { + const tag = new Tag("tag", "prog", Types.DINT); + + const group = new TagGroup(); + + expect(group.contains(tag)).toBeFalsy(); + + group.add(tag); + + expect(group.contains(tag)).toBeTruthy(); + + group.remove(tag); + + expect(group.contains(tag)).toBeFalsy(); + }); + }); });