提交 41fdab39 编写于 作者: Huan (李卓桓)'s avatar Huan (李卓桓)

switch from Watchrat to NPM Watchdog

上级 3a9f20b6
......@@ -101,6 +101,7 @@
"retry-promise": "^1.0.0",
"rxjs": "^5.4.3",
"state-switch": "^0.1.13",
"watchdog": "^0.1.3",
"ws": "^3.2.0",
"xml2js": "^0.4.19"
},
......
......@@ -16,6 +16,10 @@
* limitations under the License.
*
*/
import {
WatchdogFood,
} from 'watchdog'
import {
log,
} from '../config'
......@@ -27,9 +31,6 @@ import {
import {
ScanInfo,
} from '../puppet'
import {
WatchratFood,
} from '../watchrat'
import Firer from './firer'
import PuppetWeb from './puppet-web'
......@@ -80,10 +81,10 @@ async function onScan(this: PuppetWeb, data: ScanInfo): Promise<void> {
}
// feed watchDog a `scan` type of food
const food = {
const food: WatchdogFood = {
data,
type: 'scan',
} as WatchratFood
}
this.emit('watchdog', food)
this.emit('scan' , data.url, data.code)
}
......
......@@ -16,6 +16,11 @@
* limitations under the License.
*
*/
import {
Watchdog,
WatchdogFood,
} from 'watchdog'
import {
config,
log,
......@@ -35,10 +40,6 @@ import {
import Room from '../room'
import RxQueue from '../rx-queue'
import Misc from '../misc'
import {
Watchrat,
WatchratFood,
} from '../watchrat'
import {
Bridge,
......@@ -62,8 +63,8 @@ export class PuppetWeb extends Puppet {
public bridge : Bridge
public scanInfo : ScanInfo | null
public puppetWatchrat : Watchrat<PuppetFoodType>
public scanWatchrat : Watchrat<ScanFoodType>
public puppetWatchdog : Watchdog<PuppetFoodType>
public scanWatchdog : Watchdog<ScanFoodType>
private fileId : number
......@@ -74,10 +75,10 @@ export class PuppetWeb extends Puppet {
this.fileId = 0
const PUPPET_TIMEOUT = 60 * 1000 // 1 minute
this.puppetWatchrat = new Watchrat<PuppetFoodType>('PuppetWeb', PUPPET_TIMEOUT)
this.puppetWatchdog = new Watchdog<PuppetFoodType>(PUPPET_TIMEOUT, 'PuppetWeb')
const SCAN_TIMEOUT = 4 * 60 * 1000 // 4 minutes
this.scanWatchrat = new Watchrat<ScanFoodType>('Scan', SCAN_TIMEOUT)
this.scanWatchdog = new Watchdog<ScanFoodType>(SCAN_TIMEOUT, 'Scan')
}
public toString() { return `Class PuppetWeb({profile:${this.options.profile}})` }
......@@ -106,7 +107,7 @@ export class PuppetWeb extends Puppet {
*/
this.state.current('live')
const food: WatchratFood = {
const food: WatchdogFood = {
data: 'inited',
timeout: 2 * 60 * 1000, // 2 mins for first login
}
......@@ -136,9 +137,9 @@ export class PuppetWeb extends Puppet {
public initWatchdogForPuppet(): void {
const puppet = this
const dog = this.puppetWatchrat
const dog = this.puppetWatchdog
puppet.on('ding', data => this.puppetWatchrat.feed({
puppet.on('ding', data => this.puppetWatchdog.feed({
data,
type: 'ding',
}))
......@@ -163,7 +164,7 @@ export class PuppetWeb extends Puppet {
*/
public initWatchdogForScan(): void {
const puppet = this
const dog = this.scanWatchrat
const dog = this.scanWatchdog
puppet.on('scan', info => dog.feed({
data: info,
......@@ -189,7 +190,7 @@ export class PuppetWeb extends Puppet {
try {
await this.bridge.reload()
} catch (e) {
log.error('PuppetWeb', 'initScanWatchrat() on(reset) exception: %s', e)
log.error('PuppetWeb', 'initScanWatchdog() on(reset) exception: %s', e)
this.emit('error', e)
}
})
......@@ -214,7 +215,7 @@ export class PuppetWeb extends Puppet {
}
log.verbose('PuppetWeb', 'quit() kill watchdog before do quit')
this.puppetWatchrat.sleep()
this.puppetWatchdog.sleep()
this.state.target('dead')
this.state.current('dead', false)
......
......@@ -19,6 +19,9 @@
import { EventEmitter } from 'events'
import { StateSwitch } from 'state-switch'
import {
WatchdogFood,
} from 'watchdog'
import {
Sayable,
......@@ -32,9 +35,6 @@ import {
} from './message'
import Profile from './profile'
import Room from './room'
import {
WatchratFood,
} from './watchrat'
import {
WechatyEvent,
} from './wechaty'
......@@ -76,7 +76,7 @@ export abstract class Puppet extends EventEmitter implements Sayable {
public emit(event: 'room-leave', room: Room, leaverList: Contact[]) : boolean
public emit(event: 'room-topic', room: Room, topic: string, oldTopic: string, changer: Contact) : boolean
public emit(event: 'scan', url: string, code: number) : boolean
public emit(event: 'watchdog', food: WatchratFood) : boolean
public emit(event: 'watchdog', food: WatchdogFood) : boolean
public emit(event: never, ...args: any[]) : boolean
public emit(
......@@ -97,7 +97,7 @@ export abstract class Puppet extends EventEmitter implements Sayable {
public on(event: 'room-leave', listener: (room: Room, leaverList: Contact[]) => void) : this
public on(event: 'room-topic', listener: (room: Room, topic: string, oldTopic: string, changer: Contact) => void) : this
public on(event: 'scan', listener: (info: ScanInfo) => void) : this
public on(event: 'watchdog', listener: (data: WatchratFood) => void) : this
public on(event: 'watchdog', listener: (data: WatchdogFood) => void) : this
public on(event: never, listener: any) : this
public on(
......
#!/usr/bin/env ts-node
// tslint:disable:no-shadowed-variable
import * as test from 'blue-tape'
import * as sinon from 'sinon'
const sinonTest = require('sinon-test')(sinon)
// import { log } from './config'
// log.level('silly')
import {
Watchrat,
WatchratFood,
} from './watchrat'
test('starve to death', sinonTest(async function(t) {
const TIMEOUT = 1 * 1000
const EXPECTED_FOOD = {
data : 'dummy',
timeout : TIMEOUT,
} as WatchratFood
const watchrat = new Watchrat('TestWatchrat', TIMEOUT)
watchrat.on('reset', (food, left) => {
t.equal(left, 0, 'timeLeft should equal to 0 when reset')
t.deepEqual(food, EXPECTED_FOOD, 'should get food back when reset')
})
watchrat.feed(EXPECTED_FOOD)
this.clock.tick(TIMEOUT + 1)
t.end()
}))
test('feed in the middle', sinonTest(async function(t) {
// console.log('this', this)
const TIMEOUT = 1 * 1000
const FEED_TIME = 0.3 * 1000
const watchrat = new Watchrat('TestWatchrat', TIMEOUT)
watchrat.on('reset', () => {
t.fail('should not be reset')
})
watchrat.feed({ data: 'dummy' })
this.clock.tick(FEED_TIME)
const left = watchrat.feed({ data: 'dummy' })
t.equal(left, TIMEOUT - FEED_TIME, 'should get the time left dependes on the FEED_TIME')
t.end()
}))
test('sleep()', sinonTest(async function(t) {
const TIMEOUT = 1 * 1000
const FEED_TIME = 0.3 * 1000
const watchrat = new Watchrat('TestWatchrat', TIMEOUT)
watchrat.on('reset', () => {
t.fail('should not be reset')
})
watchrat.feed({ data: 'dummy' })
this.clock.tick(FEED_TIME)
watchrat.sleep()
this.clock.tick(TIMEOUT * 2)
const left = watchrat.left()
t.ok(left < 0, 'time should already passed by...')
t.end()
}))
import { EventEmitter } from 'events'
import { log } from './config'
export type WatchratEvent = 'feed' | 'reset' | 'sleep'
export type WatchratListener = (food: WatchratFood, left: number) => void
export interface WatchratFood<T = any> {
data : any,
timeout? : number, // millisecond
type? : T
}
export class Watchrat<T = any> extends EventEmitter {
private timer : NodeJS.Timer | undefined | null // `undefined` stands for the first time init. `null` will be set by `stopTimer`
private lastFeed : number
private lastFood : WatchratFood<T>
constructor(
public name = 'Meow',
public defaultTimeout = 60 * 1000,
) {
super()
log.verbose('Watchrat', '%s: constructor(name=%s, defaultTimeout=%d)', name, name, defaultTimeout)
}
public on(event: 'feed', listener: WatchratListener) : this
public on(event: 'reset', listener: WatchratListener) : this
public on(event: 'sleep', listener: WatchratListener) : this
public on(event: never, listener: never) : never
public on(event: WatchratEvent, listener: WatchratListener): this {
log.verbose('Watchrat', '%s: on(%s)', this.name, event)
super.on(event, listener)
return this
}
private startTimer(timeout: number): void {
log.verbose('Watchrat', '%s: startTimer()', this.name)
if (this.timer) {
throw new Error('timer already exist!')
}
this.timer = setTimeout(() => {
log.verbose('Watchrat', '%s: startTimer() setTimeout() after %d', this.name, this.defaultTimeout)
this.emit('reset', this.lastFood, 0)
}, timeout)
this.timer.unref() // should not block node quit
return
}
private stopTimer(sleep = false): void {
log.verbose('Watchrat', '%s: stopTimer()', this.name)
if (typeof this.timer === 'undefined') { // first time
log.verbose('Watchrat', '%s: stopTimer() first run(or after sleep)', this.name)
return
}
if (this.timer) {
clearTimeout(this.timer)
this.timer = null
} else if (!sleep) {
throw new Error('timer is already stoped!')
}
}
public left(): number {
let left
if (Number.isInteger(this.lastFeed)) {
// console.log('lastFeed=', this.lastFeed)
// console.log('timeout=', this.lastFood.timeout)
// console.log('Date.now()=', Date.now())
left = this.lastFeed + this.defaultTimeout - Date.now()
log.verbose('Watchrat', '%s: timerLeft() = %d', this.name, left)
} else {
left = 0
log.verbose('Watchrat', '%s: timerLeft() first feed, left=%s', this.name, left)
}
return left
}
public feed(food: WatchratFood<T>): number {
log.verbose('Watchrat', '%s: feed(%s)', this.name, food)
if (!food.timeout) {
food.timeout = this.defaultTimeout
}
const left = this.left()
this.stopTimer()
this.startTimer(food.timeout)
this.lastFeed = Date.now()
this.lastFood = food
this.emit('feed', food, left)
return left
}
public sleep(): void {
log.verbose('Watchrat', '%s: sleep()', this.name)
this.stopTimer(true)
this.timer = undefined
}
}
export default Watchrat
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册