Просмотр исходного кода

:recycle: move some things around

master
toj 2 лет назад
Родитель
Сommit
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 Просмотреть файл

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
-/**
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
 //     channelOrSensor,
6
 //     channelOrSensor,
7
 // })
7
 // })
8
 
8
 
9
+/**
10
+ * Built-in controller types
11
+ *
12
+ * @type {{ temp: KnownControllerType; level: KnownControllerType; }}
13
+ */
9
 const controllerTypes = {
14
 const controllerTypes = {
10
     temp: 'temperature',
15
     temp: 'temperature',
11
     level: 'water_level',
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
 class Controller {
25
 class Controller {
20
-    constructor({ type, channel }) {
26
+    constructor({ type, channel }, onUpdate) {
21
         this._type = type
27
         this._type = type
22
         this.channel = channel
28
         this.channel = channel
23
     }
29
     }
27
     get inRange() {
33
     get inRange() {
28
         return this.channel.inRange
34
         return this.channel.inRange
29
     }
35
     }
36
+    /**
37
+     * Returns from Reading.val callback
38
+     *
39
+     * @returns {*}
40
+     */
30
     update() {
41
     update() {
31
-        this.channel = this.channel.update()
42
+        this.channel = updateChannel(this.channel)
32
         return this.channel.val
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
 export { Controller, controllerTypes }
53
 export { Controller, controllerTypes }

+ 100
- 0
src/core/channel.js Просмотреть файл

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
 import { nanoid } from 'nanoid'
1
 import { nanoid } from 'nanoid'
2
 import { toLiquid } from './utils.js'
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
 class Container {
11
 class Container {
5
     constructor({ l, w, h }, level = 0, label, id = nanoid()) {
12
     constructor({ l, w, h }, level = 0, label, id = nanoid()) {
6
         this._id = id //=> "V1StGXR8_Z5jdHi6B-myT"
13
         this._id = id //=> "V1StGXR8_Z5jdHi6B-myT"
16
     get label() {
23
     get label() {
17
         return this._label
24
         return this._label
18
     }
25
     }
26
+    get dims() {
27
+        return { l: this.l, w: this.w, h: this.h }
28
+    }
19
     get liquidVolumeCapacity() {
29
     get liquidVolumeCapacity() {
20
         return toLiquid(this.l * this.w * this.h)
30
         return toLiquid(this.l * this.w * this.h)
21
     }
31
     }

+ 7
- 0
src/core/utils.js Просмотреть файл

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
 
2
 
3
 import loop from './loop.js'
3
 import loop from './loop.js'
4
 import { System, makeContainerT } from './system.js'
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
 const fromConfig = {
8
 const fromConfig = {
16
     tank_1: {
9
     tank_1: {
17
         inputs: [],
10
         inputs: [],
36
         const containerT = makeContainerT(
29
         const containerT = makeContainerT(
37
             inputs,
30
             inputs,
38
             outputs,
31
             outputs,
39
-            new Container(dims, 0, containerName)
32
+            new Container(dims, 0, containerName),
40
         )
33
         )
41
         sys.add(containerT[2].id, containerT)
34
         sys.add(containerT[2].id, containerT)
42
     }
35
     }

+ 25
- 0
src/input.js Просмотреть файл

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
-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
  * Object that holds each container and their input and outputs
6
  * Object that holds each container and their input and outputs
6
  *
7
  *
7
- * @type {System}
8
+ * @typedef {System}
8
  */
9
  */
9
 class System {
10
 class System {
10
     constructor() {
11
     constructor() {
24
         this.inventory[container.id] = makeContainerT(
25
         this.inventory[container.id] = makeContainerT(
25
             inputs,
26
             inputs,
26
             outputs,
27
             outputs,
27
-            container
28
+            container,
28
         )
29
         )
29
     }
30
     }
30
     add(id, containerT) {
31
     add(id, containerT) {
35
     }
36
     }
36
     inputsFor({ id }) {
37
     inputsFor({ id }) {
37
         return Object.values(this.inventory).find(
38
         return Object.values(this.inventory).find(
38
-            containerT => containerT[2].id == id
39
+            containerT => containerT[2].id == id,
39
         )[0]
40
         )[0]
40
     }
41
     }
41
     outputsFor({ id }) {
42
     outputsFor({ id }) {
42
         return Object.values(this.inventory).find(
43
         return Object.values(this.inventory).find(
43
-            containerT => containerT[2].id == id
44
+            containerT => containerT[2].id == id,
44
         )[1]
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
 const makeContainerT = (inputs, outputs, container) => {
57
 const makeContainerT = (inputs, outputs, container) => {
74
-    const controllers = inputs.map(_makeControllerFromInputConfig)
58
+    const controllers = inputs.map(makeController)
75
     return [controllers, outputs, container]
59
     return [controllers, outputs, container]
76
 }
60
 }
77
 
61
 

+ 0
- 37
src/utils.js Просмотреть файл

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
 import { expect, test } from 'vitest'
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
 test('channel - instantiate channel with readers correctly', () => {
4
 test('channel - instantiate channel with readers correctly', () => {
6
     let now = Date.now()
5
     let now = Date.now()
19
     expect(testChan.unit).toStrictEqual('F')
18
     expect(testChan.unit).toStrictEqual('F')
20
 
19
 
21
     mockTemp = 101
20
     mockTemp = 101
22
-    testChan = testChan.update()
21
+    testChan = updateChannel(testChan)
23
     expect(testChan.lastUpdate > now).toStrictEqual(true)
22
     expect(testChan.lastUpdate > now).toStrictEqual(true)
24
     expect(testChan.val).toStrictEqual(101)
23
     expect(testChan.val).toStrictEqual(101)
25
     expect(testChan.inRange).toStrictEqual(false)
24
     expect(testChan.inRange).toStrictEqual(false)
27
     expect(testChan.belowRange).toStrictEqual(false)
26
     expect(testChan.belowRange).toStrictEqual(false)
28
 
27
 
29
     mockTemp = 49
28
     mockTemp = 49
30
-    testChan = testChan.update()
29
+    testChan = updateChannel(testChan)
31
     now = Date.now()
30
     now = Date.now()
32
     expect(testChan.lastUpdate == now).toStrictEqual(true)
31
     expect(testChan.lastUpdate == now).toStrictEqual(true)
33
     expect(testChan.inRange).toStrictEqual(false)
32
     expect(testChan.inRange).toStrictEqual(false)

+ 1
- 1
test/container.spec.js Просмотреть файл

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

+ 45
- 0
test/controller.spec.js Просмотреть файл

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

+ 2
- 2
test/utils.spec.js Просмотреть файл

1
 import { expect, test } from 'vitest'
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
 const dims = { l: 10, w: 10, h: 10 } // inches
5
 const dims = { l: 10, w: 10, h: 10 } // inches
6
 const capacity = 4.329004329004329 // gallons
6
 const capacity = 4.329004329004329 // gallons

Загрузка…
Отмена
Сохранить