瀏覽代碼

:recycle: move some things around

master
toj 2 年之前
父節點
當前提交
d60010d159
共有 15 個檔案被更改,包括 290 行新增162 行删除
  1. 55
    0
      src/api.js
  2. 0
    67
      src/channel.js
  3. 22
    8
      src/controller.js
  4. 100
    0
      src/core/channel.js
  5. 10
    0
      src/core/container.js
  6. 7
    0
      src/core/utils.js
  7. 3
    10
      src/index.js
  8. 25
    0
      src/input.js
  9. 15
    31
      src/system.js
  10. 0
    37
      src/utils.js
  11. 3
    4
      test/channel.spec.js
  12. 1
    1
      test/container.spec.js
  13. 45
    0
      test/controller.spec.js
  14. 2
    2
      test/system.spec.js
  15. 2
    2
      test/utils.spec.js

+ 55
- 0
src/api.js 查看文件

@@ -0,0 +1,55 @@
1
+import { fromLiquid } from './core/utils.js'
2
+
3
+import { Container } from './core/container.js'
4
+import { Reading, Channel } from './core/channel'
5
+import { Controller } from './core/controller'
6
+import { InputConf } from './input.js'
7
+
8
+/**
9
+ * Changing level gives you back a new Container
10
+ * object to keep you stateless.
11
+ *
12
+ * @param {Number} amount
13
+ * @param {Container} container
14
+ * @returns {Container}
15
+ */
16
+const _changeLevel = (amount, container) => {
17
+    const updatedLevel =
18
+        fromLiquid(container.liquidVolumeFilled + amount) /
19
+        container.l /
20
+        container.w
21
+    return new Container(
22
+        container.dims,
23
+        updatedLevel,
24
+        container.label,
25
+        container.id,
26
+    )
27
+}
28
+
29
+/**
30
+ * Take in inputConf objects (see: index.js)
31
+ * and returns Controllers with an initial reading
32
+ *
33
+ * @param {InputConf}
34
+ * @returns {Controller}
35
+ */
36
+const makeController = ({ type, onRead, interval, onUpdate }) => {
37
+    const reader = new Reading({
38
+        onRead,
39
+        unit: 'F',
40
+        max: 80,
41
+        min: 72,
42
+    })
43
+
44
+    const controller = new Controller({
45
+        type,
46
+        channel: new Channel({ interval, reader }),
47
+    })
48
+    controller.onUpdate = onUpdate
49
+    return controller
50
+}
51
+
52
+const fill = ({ container, amount }) => _changeLevel(amount, container)
53
+const drain = ({ container, amount }) => _changeLevel(-1 * amount, container)
54
+
55
+export { fill, drain, makeController }

+ 0
- 67
src/channel.js 查看文件

@@ -1,67 +0,0 @@
1
-/**
2
- * @type {Reading}
3
- */
4
-class Reading {
5
-    constructor({ onRead, unit, min, max }) {
6
-        this._val = onRead
7
-        this.unit = unit
8
-        this.max = max
9
-        this.min = min
10
-        this.created = Date.now()
11
-    }
12
-    /**
13
-     * Reach out and grabs the value
14
-     * from some sensor
15
-     */
16
-    get val() {
17
-        return this._val()
18
-    }
19
-}
20
-
21
-/**
22
- * @type {Channel}
23
- */
24
-class Channel {
25
-    constructor({ interval = 1, reader = null }) {
26
-        this._interval = interval
27
-        this._reader = reader
28
-    }
29
-    get interval() {
30
-        return this._interval
31
-    }
32
-    get lastUpdate() {
33
-        return this._reader.created
34
-    }
35
-    get val() {
36
-        return this._reader.val
37
-    }
38
-    get unit() {
39
-        return this._reader.unit
40
-    }
41
-    get aboveRange() {
42
-        return this._reader.val > this._reader.max
43
-    }
44
-    get belowRange() {
45
-        return this._reader.val < this._reader.min
46
-    }
47
-    get inRange() {
48
-        return this._reader.val && !this.aboveRange && !this.belowRange
49
-            ? true
50
-            : false
51
-    }
52
-    /** Always return a new Reading instance with new Channel instances */
53
-    update() {
54
-        const reader = new Reading({
55
-            // !: Setup the callback to return a sensor value
56
-            onRead: this._reader._val,
57
-            unit: this._reader.unit,
58
-            max: this._reader.max,
59
-            min: this._reader.min,
60
-        })
61
-
62
-        const chan = new Channel({ interval: this.interval, reader })
63
-        return chan
64
-    }
65
-}
66
-
67
-export { Channel, Reading }

+ 22
- 8
src/controller.js 查看文件

@@ -6,18 +6,24 @@ let allWarnings = []
6 6
 //     channelOrSensor,
7 7
 // })
8 8
 
9
+/**
10
+ * Built-in controller types
11
+ *
12
+ * @type {{ temp: KnownControllerType; level: KnownControllerType; }}
13
+ */
9 14
 const controllerTypes = {
10 15
     temp: 'temperature',
11 16
     level: 'water_level',
12 17
 }
13 18
 
14 19
 /**
15
- * Object to link a channel to an action
20
+ * Object to link a Channel to an onUpdate action.
16 21
  *
17
- * @type {Controller} links together channels
18
- **/
22
+ * @class Controller
23
+ * @typedef {Controller}
24
+ */
19 25
 class Controller {
20
-    constructor({ type, channel }) {
26
+    constructor({ type, channel }, onUpdate) {
21 27
         this._type = type
22 28
         this.channel = channel
23 29
     }
@@ -27,13 +33,21 @@ class Controller {
27 33
     get inRange() {
28 34
         return this.channel.inRange
29 35
     }
36
+    /**
37
+     * Returns from Reading.val callback
38
+     *
39
+     * @returns {*}
40
+     */
30 41
     update() {
31
-        this.channel = this.channel.update()
42
+        this.channel = updateChannel(this.channel)
32 43
         return this.channel.val
33 44
     }
34
-    onUpdate() {
35
-        // Not implemented here
36
-    }
45
+    /**
46
+     * Control logic to perform.
47
+     *
48
+     * @overload
49
+     */
50
+    onUpdate() {}
37 51
 }
38 52
 
39 53
 export { Controller, controllerTypes }

+ 100
- 0
src/core/channel.js 查看文件

@@ -0,0 +1,100 @@
1
+/**
2
+ * Readers actually reach out and get values for a Channel.
3
+ * You probably should never instantiate this outside of testing.
4
+ *
5
+ * @private
6
+ * @class Reading
7
+ * @typedef {Reading}
8
+ */
9
+class Reading {
10
+    /**
11
+     * Creates an instance of Reading.
12
+     *
13
+     * @constructor
14
+     * @param {{ onRead: function; unit: any; min: any; max: any; }} param0
15
+     * @param {function} param0.onRead
16
+     * @param {string} param0.unit
17
+     * @param {*} param0.min
18
+     * @param {*} param0.max
19
+     */
20
+    constructor({ onRead, unit, min, max }) {
21
+        this._readVal = onRead
22
+        this.unit = unit
23
+        this.max = max
24
+        this.min = min
25
+        this.created = Date.now()
26
+    }
27
+    /**
28
+     * Reach out and grabs the value from some sensor
29
+     *
30
+     * @readonly
31
+     * @type {*}
32
+     */
33
+    get val() {
34
+        return this._readVal()
35
+    }
36
+}
37
+
38
+/**
39
+ *
40
+ * @param {{ onRead: function, unit: any; min: any; max: any; interval: any; }} param0
41
+ * @returns {Channel}
42
+ */
43
+const updateChannel = ({ onRead, unit, max, min, interval }) =>
44
+    new Channel({
45
+        interval,
46
+        reader: new Reading({
47
+            onRead,
48
+            unit,
49
+            max,
50
+            min,
51
+        }),
52
+    })
53
+
54
+/**
55
+ * Channels provide and interface to Readings to
56
+ * grab values and provide some simple getter based
57
+ * on the value.
58
+ *
59
+ * @class Channel
60
+ * @typedef {Channel}
61
+ */
62
+class Channel {
63
+    /**
64
+     * Creates an instance of Channel.
65
+     *
66
+     * @constructor
67
+     * @param {{ interval?: number; reader?: Reading; }} ReadParams
68
+     * @param {number} [ReadParams.interval=1]
69
+     * @param {Reading} [ReadParams.reader=null]
70
+     */
71
+    constructor({ interval = 1, reader = null }) {
72
+        this._interval = interval
73
+        this._reader = reader
74
+    }
75
+    get interval() {
76
+        return this._interval
77
+    }
78
+    get lastUpdate() {
79
+        return this._reader.created
80
+    }
81
+    get val() {
82
+        return this._reader.val
83
+    }
84
+    get unit() {
85
+        return this._reader.unit
86
+    }
87
+    get aboveRange() {
88
+        return this._reader.val > this._reader.max
89
+    }
90
+    get belowRange() {
91
+        return this._reader.val < this._reader.min
92
+    }
93
+    get inRange() {
94
+        return this._reader.val && !this.aboveRange && !this.belowRange
95
+            ? true
96
+            : false
97
+    }
98
+}
99
+
100
+export { updateChannel, Channel, Reading }

src/container.js → src/core/container.js 查看文件

@@ -1,6 +1,13 @@
1 1
 import { nanoid } from 'nanoid'
2 2
 import { toLiquid } from './utils.js'
3 3
 
4
+/**
5
+ * A container instance represents any rectangular
6
+ * volume that can be filled with fluid
7
+ *
8
+ * @class Container
9
+ * @typedef {Container}
10
+ */
4 11
 class Container {
5 12
     constructor({ l, w, h }, level = 0, label, id = nanoid()) {
6 13
         this._id = id //=> "V1StGXR8_Z5jdHi6B-myT"
@@ -16,6 +23,9 @@ class Container {
16 23
     get label() {
17 24
         return this._label
18 25
     }
26
+    get dims() {
27
+        return { l: this.l, w: this.w, h: this.h }
28
+    }
19 29
     get liquidVolumeCapacity() {
20 30
         return toLiquid(this.l * this.w * this.h)
21 31
     }

+ 7
- 0
src/core/utils.js 查看文件

@@ -0,0 +1,7 @@
1
+const US_LIQUID_GALLONS_TO_CUBIC_INCHES = 231
2
+const CUBIC_INCHES_TO_US_LIQUID_GALLONS = 1 / US_LIQUID_GALLONS_TO_CUBIC_INCHES
3
+
4
+const toLiquid = cubicInches => cubicInches * CUBIC_INCHES_TO_US_LIQUID_GALLONS
5
+const fromLiquid = gallons => gallons * US_LIQUID_GALLONS_TO_CUBIC_INCHES
6
+
7
+export { toLiquid, fromLiquid }

+ 3
- 10
src/index.js 查看文件

@@ -2,16 +2,9 @@ import { EventEmitter } from 'node:events'
2 2
 
3 3
 import loop from './loop.js'
4 4
 import { System, makeContainerT } from './system.js'
5
-import { Container } from './container.js'
5
+import { InputConf } from './input.js'
6
+import { Container } from './core/container.js'
6 7
 
7
-class InputConf {
8
-    constructor(type, onRead, readInterval, onUpdate) {
9
-        ;(this.type = type),
10
-            (this.onRead = onRead),
11
-            (this.interval = readInterval),
12
-            (this.onUpdate = onUpdate)
13
-    }
14
-}
15 8
 const fromConfig = {
16 9
     tank_1: {
17 10
         inputs: [],
@@ -36,7 +29,7 @@ const digestConfig = config => {
36 29
         const containerT = makeContainerT(
37 30
             inputs,
38 31
             outputs,
39
-            new Container(dims, 0, containerName)
32
+            new Container(dims, 0, containerName),
40 33
         )
41 34
         sys.add(containerT[2].id, containerT)
42 35
     }

+ 25
- 0
src/input.js 查看文件

@@ -0,0 +1,25 @@
1
+/**
2
+ * Describe a single input for a Container.
3
+ *
4
+ * @class InputConf
5
+ * @typedef {InputConf}
6
+ */
7
+class InputConf {
8
+    /**
9
+     * Creates an instance of InputConf.
10
+     *
11
+     * @constructor
12
+     * @param {KnownControllerType} type
13
+     * @param {function} onRead
14
+     * @param {number|float} readInterval
15
+     * @param {function} onUpdate
16
+     */
17
+    constructor(type, onRead, readInterval, onUpdate) {
18
+        ;(this.type = type),
19
+            (this.onRead = onRead),
20
+            (this.interval = readInterval),
21
+            (this.onUpdate = onUpdate)
22
+    }
23
+}
24
+
25
+export { InputConf }

+ 15
- 31
src/system.js 查看文件

@@ -1,10 +1,11 @@
1
-import { Reading, Channel } from './channel'
2
-import { Controller } from './controller'
1
+import { Container } from './core/container'
2
+import { makeController } from './api'
3
+import { InputConf } from './input'
3 4
 
4 5
 /**
5 6
  * Object that holds each container and their input and outputs
6 7
  *
7
- * @type {System}
8
+ * @typedef {System}
8 9
  */
9 10
 class System {
10 11
     constructor() {
@@ -24,7 +25,7 @@ class System {
24 25
         this.inventory[container.id] = makeContainerT(
25 26
             inputs,
26 27
             outputs,
27
-            container
28
+            container,
28 29
         )
29 30
     }
30 31
     add(id, containerT) {
@@ -35,43 +36,26 @@ class System {
35 36
     }
36 37
     inputsFor({ id }) {
37 38
         return Object.values(this.inventory).find(
38
-            containerT => containerT[2].id == id
39
+            containerT => containerT[2].id == id,
39 40
         )[0]
40 41
     }
41 42
     outputsFor({ id }) {
42 43
         return Object.values(this.inventory).find(
43
-            containerT => containerT[2].id == id
44
+            containerT => containerT[2].id == id,
44 45
         )[1]
45 46
     }
46 47
 }
47 48
 
48 49
 /**
49
- * Take in inputConf objects (see: index.js)
50
- * and returns Controllers with an initial reading
51
- **/
52
-const _makeControllerFromInputConfig = ({
53
-    type,
54
-    onRead,
55
-    interval,
56
-    onUpdate,
57
-}) => {
58
-    const reader = new Reading({
59
-        onRead,
60
-        unit: 'F',
61
-        max: 80,
62
-        min: 72,
63
-    })
64
-
65
-    const controller = new Controller({
66
-        type,
67
-        channel: new Channel({ interval, reader }),
68
-    })
69
-    controller.onUpdate = onUpdate
70
-    return controller
71
-}
72
-
50
+ * Associate inputs and outputs with a Container
51
+ *
52
+ * @param {Array.<InputConf>} inputs
53
+ * @param {*} outputs
54
+ * @param {Container} container
55
+ * @returns {Array.}
56
+ */
73 57
 const makeContainerT = (inputs, outputs, container) => {
74
-    const controllers = inputs.map(_makeControllerFromInputConfig)
58
+    const controllers = inputs.map(makeController)
75 59
     return [controllers, outputs, container]
76 60
 }
77 61
 

+ 0
- 37
src/utils.js 查看文件

@@ -1,37 +0,0 @@
1
-import { Container } from './container.js'
2
-
3
-const US_LIQUID_GALLONS_TO_CUBIC_INCHES = 231
4
-const CUBIC_INCHES_TO_US_LIQUID_GALLONS = 1 / US_LIQUID_GALLONS_TO_CUBIC_INCHES
5
-
6
-const toLiquid = cubicInches => cubicInches * CUBIC_INCHES_TO_US_LIQUID_GALLONS
7
-const fromLiquid = gallons => gallons * US_LIQUID_GALLONS_TO_CUBIC_INCHES
8
-
9
-const _changeLevel = (amount, container) => {
10
-    const updatedLevel =
11
-        fromLiquid(container.liquidVolumeFilled + amount) /
12
-        container.l /
13
-        container.w
14
-    return new Container(
15
-        {
16
-            l: container.l,
17
-            w: container.w,
18
-            h: container.h,
19
-        },
20
-        updatedLevel,
21
-        container.label,
22
-        container.id
23
-    )
24
-}
25
-
26
-// Human API
27
-const fill = ({ container, amount }) => _changeLevel(amount, container)
28
-const drain = ({ container, amount }) => _changeLevel(-1 * amount, container)
29
-
30
-const interpolate = ({ x, y, from }) => {
31
-    const [x1, x2] = x
32
-    const [y1, y2] = y
33
-    const to = y1 + ((y2 - y1) / (x2 - x1)) * (from - x1)
34
-    return to
35
-}
36
-
37
-export { toLiquid, fromLiquid, fill, drain, interpolate }

+ 3
- 4
test/channel.spec.js 查看文件

@@ -1,6 +1,5 @@
1 1
 import { expect, test } from 'vitest'
2
-import { Channel } from '../src/channel.js'
3
-import { Reading } from '../src/channel.js'
2
+import { updateChannel, Channel, Reading } from '../src/core/channel.js'
4 3
 
5 4
 test('channel - instantiate channel with readers correctly', () => {
6 5
     let now = Date.now()
@@ -19,7 +18,7 @@ test('channel - instantiate channel with readers correctly', () => {
19 18
     expect(testChan.unit).toStrictEqual('F')
20 19
 
21 20
     mockTemp = 101
22
-    testChan = testChan.update()
21
+    testChan = updateChannel(testChan)
23 22
     expect(testChan.lastUpdate > now).toStrictEqual(true)
24 23
     expect(testChan.val).toStrictEqual(101)
25 24
     expect(testChan.inRange).toStrictEqual(false)
@@ -27,7 +26,7 @@ test('channel - instantiate channel with readers correctly', () => {
27 26
     expect(testChan.belowRange).toStrictEqual(false)
28 27
 
29 28
     mockTemp = 49
30
-    testChan = testChan.update()
29
+    testChan = updateChannel(testChan)
31 30
     now = Date.now()
32 31
     expect(testChan.lastUpdate == now).toStrictEqual(true)
33 32
     expect(testChan.inRange).toStrictEqual(false)

+ 1
- 1
test/container.spec.js 查看文件

@@ -1,5 +1,5 @@
1 1
 import { expect, test } from 'vitest'
2
-import { Container } from '../src/container.js'
2
+import { Container } from '../src/core/container.js'
3 3
 
4 4
 const dims = { l: 10, w: 10, h: 10 } // inches
5 5
 const capacity = 4.329004329004329 // gallons

+ 45
- 0
test/controller.spec.js 查看文件

@@ -0,0 +1,45 @@
1
+import { expect, test } from 'vitest'
2
+import { Channel, Reading } from '../src/core/channel'
3
+import { Controller } from '../src/controller'
4
+
5
+test('controller - updates correctly', () => {
6
+    let mockTemp = 70
7
+    let testReader = new Reading({
8
+        onRead: () => mockTemp,
9
+        unit: 'F',
10
+        max: 100,
11
+        min: 50,
12
+    })
13
+    let testChan = new Channel({ interval: 1, reader: testReader })
14
+
15
+    const testController = new Controller({
16
+        type: 'blah',
17
+        channel: testChan,
18
+    })
19
+    expect(testController.inRange).toBe(true)
20
+    mockTemp = 101
21
+    testController.update()
22
+    expect(testController.inRange).toBe(false)
23
+
24
+    testController.onUpdate = foo => 'foo'
25
+    expect(testController.onUpdate('bar')).toBe('foo')
26
+})
27
+
28
+test('controller - onUpdate overloading works', () => {
29
+    let mockTemp = 70
30
+    let testReader = new Reading({
31
+        onRead: () => mockTemp,
32
+        unit: 'F',
33
+        max: 100,
34
+        min: 50,
35
+    })
36
+    let testChan = new Channel({ interval: 1, reader: testReader })
37
+
38
+    const testController = new Controller({
39
+        type: 'blah',
40
+        channel: testChan,
41
+    })
42
+
43
+    testController.onUpdate = foo => 'foo'
44
+    expect(testController.onUpdate('bar')).toBe('foo')
45
+})

+ 2
- 2
test/system.spec.js 查看文件

@@ -1,6 +1,6 @@
1 1
 import { expect, test } from 'vitest'
2
+import { Container } from '../src/core/container.js'
2 3
 import { System, makeContainerT } from '../src/system.js'
3
-import { Container } from '../src/container.js'
4 4
 import { controllerTypes } from '../src/controller.js'
5 5
 import { InputConf } from '../src/index.js'
6 6
 
@@ -32,7 +32,7 @@ test('system - update channels with readers correctly', () => {
32 32
         controllerTypes.temp,
33 33
         () => mockTemp,
34 34
         0.1,
35
-        () => 'tick'
35
+        () => 'tick',
36 36
     )
37 37
     const cT = makeContainerT([testInConf], [], testContainer)
38 38
     testSystem.add('aaa', cT)

+ 2
- 2
test/utils.spec.js 查看文件

@@ -1,6 +1,6 @@
1 1
 import { expect, test } from 'vitest'
2
-import { Container } from '../src/container.js'
3
-import { fill, drain } from '../src/utils.js'
2
+import { Container } from '../src/core/container.js'
3
+import { fill, drain } from '../src/api.js'
4 4
 
5 5
 const dims = { l: 10, w: 10, h: 10 } // inches
6 6
 const capacity = 4.329004329004329 // gallons

Loading…
取消
儲存