Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
EverestRs
Yomo
提交
2e358eeb
Y
Yomo
项目概览
EverestRs
/
Yomo
与 Fork 源项目一致
Fork自
熹乐科技 / Yomo
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
Yomo
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
2e358eeb
编写于
3月 26, 2021
作者:
weixin_53053927
提交者:
GitHub
3月 26, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(rx): Add SlidingWindow (#149)
上级
46d38d40
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
209 addition
and
2 deletion
+209
-2
example/flow/app.go
example/flow/app.go
+32
-2
pkg/rx/rxstream.go
pkg/rx/rxstream.go
+8
-0
pkg/rx/rxstream_operator.go
pkg/rx/rxstream_operator.go
+169
-0
未找到文件。
example/flow/app.go
浏览文件 @
2e358eeb
...
...
@@ -10,9 +10,18 @@ import (
"github.com/yomorun/yomo/pkg/rx"
)
// NoiseDataKey represents the Tag of a Y3 encoded data packet
// NoiseDataKey represents the Tag of a Y3 encoded data packet
.
const
NoiseDataKey
=
0x10
// ThresholdSingleValue is the threshold of a single value.
const
ThresholdSingleValue
=
60
// ThresholdAverageValue is the threshold of the average value after a sliding window.
const
ThresholdAverageValue
=
55
// SlidingWindowSeconds is the time in seconds of the sliding window.
const
SlidingWindowSeconds
=
30
// NoiseData represents the structure of data
type
NoiseData
struct
{
Noise
float32
`y3:"0x11"`
...
...
@@ -24,6 +33,11 @@ var printer = func(_ context.Context, i interface{}) (interface{}, error) {
value
:=
i
.
(
NoiseData
)
rightNow
:=
time
.
Now
()
.
UnixNano
()
/
int64
(
time
.
Millisecond
)
fmt
.
Println
(
fmt
.
Sprintf
(
"[%s] %d > value: %f ⚡️=%dms"
,
value
.
From
,
value
.
Time
,
value
.
Noise
,
rightNow
-
value
.
Time
))
if
value
.
Noise
>=
ThresholdSingleValue
{
fmt
.
Println
(
fmt
.
Sprintf
(
"❗ value: %f reaches the threshold %d!"
,
value
.
Noise
,
ThresholdSingleValue
))
}
return
value
.
Noise
,
nil
}
...
...
@@ -37,13 +51,29 @@ var callback = func(v []byte) (interface{}, error) {
return
mold
,
nil
}
var
slidingWindowHandler
=
func
(
i
interface
{})
error
{
values
,
ok
:=
i
.
([]
interface
{})
if
ok
{
var
total
float32
=
0
for
_
,
value
:=
range
values
{
total
+=
value
.
(
float32
)
}
avg
:=
total
/
float32
(
len
(
values
))
if
avg
>=
ThresholdAverageValue
{
fmt
.
Println
(
fmt
.
Sprintf
(
"❗ average value in last %d seconds: %f reaches the threshold %d!"
,
SlidingWindowSeconds
,
avg
,
ThresholdAverageValue
))
}
}
return
nil
}
// Handler will handle data in Rx way
func
Handler
(
rxstream
rx
.
RxStream
)
rx
.
RxStream
{
stream
:=
rxstream
.
Subscribe
(
NoiseDataKey
)
.
OnObserve
(
callback
)
.
Debounce
(
rxgo
.
WithDuration
(
50
*
time
.
Millisecond
))
.
Debounce
(
rxgo
.
WithDuration
(
50
*
time
.
Millisecond
))
.
Map
(
printer
)
.
SlidingWindowWithTime
(
SlidingWindowSeconds
*
time
.
Second
,
1
*
time
.
Second
,
slidingWindowHandler
)
.
StdOut
()
.
Encode
(
0x11
)
...
...
pkg/rx/rxstream.go
浏览文件 @
2e358eeb
...
...
@@ -92,4 +92,12 @@ type RxStream interface {
WindowWithTime
(
timespan
rxgo
.
Duration
,
opts
...
rxgo
.
Option
)
RxStream
WindowWithTimeOrCount
(
timespan
rxgo
.
Duration
,
count
int
,
opts
...
rxgo
.
Option
)
RxStream
ZipFromIterable
(
iterable
rxgo
.
Iterable
,
zipper
rxgo
.
Func2
,
opts
...
rxgo
.
Option
)
RxStream
// SlidingWindowWithCount buffers the data in the specified sliding window size, the buffered data can be processed in the handler func.
// It returns the orginal data to RxStream, not the buffered slice.
SlidingWindowWithCount
(
windowSize
int
,
slideSize
int
,
handler
Handler
,
opts
...
rxgo
.
Option
)
RxStream
// SlidingWindowWithTime buffers the data in the specified sliding window time, the buffered data can be processed in the handler func.
// It returns the orginal data to RxStream, not the buffered slice.
SlidingWindowWithTime
(
windowTimespan
time
.
Duration
,
slideTimespan
time
.
Duration
,
handler
Handler
,
opts
...
rxgo
.
Option
)
RxStream
}
pkg/rx/rxstream_operator.go
浏览文件 @
2e358eeb
...
...
@@ -3,8 +3,10 @@ package rx
import
(
"bytes"
"context"
"errors"
"fmt"
"io"
"sync"
"time"
"github.com/cenkalti/backoff/v4"
...
...
@@ -691,6 +693,173 @@ func (s *RxStreamImpl) Encode(key byte, opts ...rxgo.Option) RxStream {
return
CreateObservable
(
f
,
opts
...
)
}
// SlidingWindowWithCount buffers the data in the specified sliding window size, the buffered data can be processed in the handler func.
// It returns the orginal data to RxStream, not the buffered slice.
func
(
s
*
RxStreamImpl
)
SlidingWindowWithCount
(
windowSize
int
,
slideSize
int
,
handler
Handler
,
opts
...
rxgo
.
Option
)
RxStream
{
if
windowSize
<=
0
{
return
s
.
thrown
(
errors
.
New
(
"windowSize must be positive"
))
}
if
slideSize
<=
0
{
return
s
.
thrown
(
errors
.
New
(
"slideSize must be positive"
))
}
f
:=
func
(
ctx
context
.
Context
,
next
chan
rxgo
.
Item
)
{
defer
close
(
next
)
observe
:=
s
.
Observe
()
windowCount
:=
0
currentSlideCount
:=
0
buf
:=
make
([]
interface
{},
windowSize
)
firstTimeSend
:=
true
mutex
:=
sync
.
Mutex
{}
for
{
select
{
case
<-
ctx
.
Done
()
:
return
case
item
,
ok
:=
<-
observe
:
if
!
ok
{
return
}
if
item
.
Error
()
{
return
}
mutex
.
Lock
()
if
windowCount
<
windowSize
{
buf
[
windowCount
]
=
item
.
V
windowCount
++
}
if
windowCount
==
windowSize
{
// start sliding
currentSlideCount
++
// append slide item to buffer
if
!
firstTimeSend
{
buf
=
append
(
buf
[
1
:
windowSize
],
item
.
V
)
}
// reach slide size
if
currentSlideCount
%
slideSize
==
0
{
err
:=
handler
(
buf
)
firstTimeSend
=
false
if
err
!=
nil
{
rxgo
.
Error
(
err
)
.
SendContext
(
ctx
,
next
)
return
}
}
}
mutex
.
Unlock
()
// immediately send the original item to downstream
Of
(
item
.
V
)
.
SendContext
(
ctx
,
next
)
}
}
}
return
CreateObservable
(
f
,
opts
...
)
}
// SlidingWindowWithTime buffers the data in the specified sliding window time, the buffered data can be processed in the handler func.
// It returns the orginal data to RxStream, not the buffered slice.
func
(
s
*
RxStreamImpl
)
SlidingWindowWithTime
(
windowTimespan
time
.
Duration
,
slideTimespan
time
.
Duration
,
handler
Handler
,
opts
...
rxgo
.
Option
)
RxStream
{
f
:=
func
(
ctx
context
.
Context
,
next
chan
rxgo
.
Item
)
{
observe
:=
s
.
Observe
()
buf
:=
make
([]
slidingWithTimeItem
,
0
)
stop
:=
make
(
chan
struct
{})
firstTimeSend
:=
true
mutex
:=
sync
.
Mutex
{}
checkBuffer
:=
func
()
{
mutex
.
Lock
()
// filter items by item
updatedBuf
:=
make
([]
slidingWithTimeItem
,
0
)
availableItems
:=
make
([]
interface
{},
0
)
t
:=
time
.
Now
()
.
Add
(
-
windowTimespan
)
for
_
,
item
:=
range
buf
{
if
item
.
timestamp
.
After
(
t
)
||
item
.
timestamp
.
Equal
(
t
)
{
updatedBuf
=
append
(
updatedBuf
,
item
)
availableItems
=
append
(
availableItems
,
item
.
data
)
}
}
buf
=
updatedBuf
// apply and send items
if
len
(
availableItems
)
!=
0
{
err
:=
handler
(
availableItems
)
if
err
!=
nil
{
rxgo
.
Error
(
err
)
.
SendContext
(
ctx
,
next
)
return
}
}
firstTimeSend
=
false
mutex
.
Unlock
()
}
go
func
()
{
defer
close
(
next
)
for
{
select
{
case
<-
stop
:
checkBuffer
()
return
case
<-
ctx
.
Done
()
:
return
case
<-
time
.
After
(
windowTimespan
)
:
if
firstTimeSend
{
checkBuffer
()
}
case
<-
time
.
After
(
slideTimespan
)
:
checkBuffer
()
}
}
}()
for
{
select
{
case
<-
ctx
.
Done
()
:
close
(
stop
)
return
case
item
,
ok
:=
<-
observe
:
if
!
ok
{
close
(
stop
)
return
}
if
item
.
Error
()
{
item
.
SendContext
(
ctx
,
next
)
close
(
stop
)
return
}
else
{
mutex
.
Lock
()
// buffer data
buf
=
append
(
buf
,
slidingWithTimeItem
{
timestamp
:
time
.
Now
(),
data
:
item
.
V
,
})
mutex
.
Unlock
()
}
// immediately send the original item to downstream
Of
(
item
.
V
)
.
SendContext
(
ctx
,
next
)
}
}
}
return
CreateObservable
(
f
,
opts
...
)
}
type
slidingWithTimeItem
struct
{
timestamp
time
.
Time
data
interface
{}
}
// Handler defines a function that handle the input value.
type
Handler
func
(
interface
{})
error
func
(
s
*
RxStreamImpl
)
thrown
(
err
error
)
RxStream
{
next
:=
make
(
chan
rxgo
.
Item
,
1
)
next
<-
rxgo
.
Error
(
err
)
defer
close
(
next
)
return
&
RxStreamImpl
{
observable
:
rxgo
.
FromChannel
(
next
)}
}
func
CreateObservable
(
f
func
(
ctx
context
.
Context
,
next
chan
rxgo
.
Item
),
opts
...
rxgo
.
Option
)
RxStream
{
next
:=
make
(
chan
rxgo
.
Item
)
ctx
:=
context
.
Background
()
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录