Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
R
RoboPLC
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
黄新宇
RoboPLC
Commits
7f0c73c2
提交
7f0c73c2
authored
3月 14, 2024
作者:
Serhij S
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
raw udp, WResult
上级
6c65eb25
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
211 行增加
和
16 行删除
+211
-16
ci.yml
.github/workflows/ci.yml
+1
-1
Cargo.toml
Cargo.toml
+4
-0
plc-modbus.rs
examples/plc-modbus.rs
+5
-3
raw-udp.rs
examples/raw-udp.rs
+104
-0
controller.rs
src/controller.rs
+17
-12
mod.rs
src/io/mod.rs
+1
-0
raw_udp.rs
src/io/raw_udp.rs
+79
-0
没有找到文件。
.github/workflows/ci.yml
浏览文件 @
7f0c73c2
...
@@ -2,7 +2,7 @@ name: CI
...
@@ -2,7 +2,7 @@ name: CI
on
:
on
:
push
:
push
:
branches
:
[
"
main
"
]
branches
:
[
"
*
"
]
pull_request
:
pull_request
:
env
:
env
:
...
...
Cargo.toml
浏览文件 @
7f0c73c2
...
@@ -32,6 +32,7 @@ serde = { version = "1.0.197", features = ["derive", "rc"] }
...
@@ -32,6 +32,7 @@ serde = { version = "1.0.197", features = ["derive", "rc"] }
serial
=
"0.4.0"
serial
=
"0.4.0"
sysinfo
=
"0.30.6"
sysinfo
=
"0.30.6"
thiserror
=
"1.0.57"
thiserror
=
"1.0.57"
tracing
=
"0.1.40"
[dev-dependencies]
[dev-dependencies]
env_logger
=
"0.11.3"
env_logger
=
"0.11.3"
...
@@ -44,3 +45,6 @@ tracing = { version = "0.1.40", features = ["log"] }
...
@@ -44,3 +45,6 @@ tracing = { version = "0.1.40", features = ["log"] }
name
=
"plc-modbus"
name
=
"plc-modbus"
path
=
"examples/plc-modbus.rs"
path
=
"examples/plc-modbus.rs"
[[example]]
name
=
"raw-udp"
path
=
"examples/raw-udp.rs"
examples/plc-modbus.rs
浏览文件 @
7f0c73c2
...
@@ -59,13 +59,13 @@ impl ModbusPuller1 {
...
@@ -59,13 +59,13 @@ impl ModbusPuller1 {
// A worker implementation, contains a single function to run which has got access to the
// A worker implementation, contains a single function to run which has got access to the
// controller context
// controller context
impl
Worker
<
Message
,
Variables
>
for
ModbusPuller1
{
impl
Worker
<
Message
,
Variables
>
for
ModbusPuller1
{
fn
run
(
&
mut
self
,
context
:
&
Context
<
Message
,
Variables
>
)
{
fn
run
(
&
mut
self
,
context
:
&
Context
<
Message
,
Variables
>
)
->
WResult
{
// this worker does not need to be subscribed to any events
// this worker does not need to be subscribed to any events
let
hub
=
context
.hub
();
let
hub
=
context
.hub
();
for
_
in
interval
(
Duration
::
from_millis
(
500
))
{
for
_
in
interval
(
Duration
::
from_millis
(
500
))
{
match
self
.sensor_mapping.read
::
<
EnvironmentSensors
>
()
{
match
self
.sensor_mapping.read
::
<
EnvironmentSensors
>
()
{
Ok
(
v
)
=>
{
Ok
(
v
)
=>
{
context
.variables
()
.
lock
()
.temperature
=
v
.temperature
;
context
.variables
()
.
write
()
.temperature
=
v
.temperature
;
hub
.send
(
Message
::
EnvSensorData
(
TtlCell
::
new_with_value
(
hub
.send
(
Message
::
EnvSensorData
(
TtlCell
::
new_with_value
(
ENV_DATA_TTL
,
ENV_DATA_TTL
,
v
,
v
,
...
@@ -76,6 +76,7 @@ impl Worker<Message, Variables> for ModbusPuller1 {
...
@@ -76,6 +76,7 @@ impl Worker<Message, Variables> for ModbusPuller1 {
}
}
}
}
}
}
Ok
(())
}
}
}
}
...
@@ -94,7 +95,7 @@ impl ModbusRelays1 {
...
@@ -94,7 +95,7 @@ impl ModbusRelays1 {
}
}
impl
Worker
<
Message
,
Variables
>
for
ModbusRelays1
{
impl
Worker
<
Message
,
Variables
>
for
ModbusRelays1
{
fn
run
(
&
mut
self
,
context
:
&
Context
<
Message
,
Variables
>
)
{
fn
run
(
&
mut
self
,
context
:
&
Context
<
Message
,
Variables
>
)
->
WResult
{
// this worker needs to be subscribed to EnvSensorData kind of events
// this worker needs to be subscribed to EnvSensorData kind of events
let
hc
=
context
let
hc
=
context
.hub
()
.hub
()
...
@@ -132,6 +133,7 @@ impl Worker<Message, Variables> for ModbusRelays1 {
...
@@ -132,6 +133,7 @@ impl Worker<Message, Variables> for ModbusRelays1 {
}
}
}
}
}
}
Ok
(())
}
}
}
}
...
...
examples/raw-udp.rs
0 → 100644
浏览文件 @
7f0c73c2
use
roboplc
::
io
::
raw_udp
::{
UdpInput
,
UdpOutput
};
use
roboplc
::
prelude
::
*
;
use
roboplc
::
time
::
interval
;
use
tracing
::{
error
,
info
};
#[derive(DataPolicy,
Clone)]
enum
Message
{
Env
(
EnvData
),
}
// A raw UDP structure, to be sent and received
//
// Raw UDP structures are used by various software, e.g. Matlab, LabView, etc. as well as by some
// fieldbus devices
#[derive(Debug,
Clone)]
#[binrw]
#[brw(little)]
struct
EnvData
{
temp
:
f64
,
hum
:
f64
,
pressure
:
f64
,
}
// A worker to collect data from incoming UDP packets
#[derive(WorkerOpts)]
#[worker_opts(name
=
"udp_in"
)]
struct
UdpIn
{}
impl
Worker
<
Message
,
()
>
for
UdpIn
{
fn
run
(
&
mut
self
,
context
:
&
Context
<
Message
,
()
>
)
->
WResult
{
let
server
=
UdpInput
::
<
EnvData
>
::
bind
(
"127.0.0.1:25000"
,
24
)
?
;
// [`UdpInput`] is an iterator of incoming UDP packets which are automatically parsed
for
data
in
server
{
match
data
{
Ok
(
data
)
=>
{
context
.hub
()
.send
(
Message
::
Env
(
data
));
}
Err
(
e
)
=>
{
error!
(
worker
=
self
.worker_name
(),
error
=%
e
,
"udp in error"
);
}
}
}
Ok
(())
}
}
// A worker to send data to a remote UDP server
// (in this example data is just sent to UDP input worker)
#[derive(WorkerOpts)]
#[worker_opts(name
=
"udp_out"
)]
struct
UdpOut
{}
impl
Worker
<
Message
,
()
>
for
UdpOut
{
fn
run
(
&
mut
self
,
_context
:
&
Context
<
Message
,
()
>
)
->
WResult
{
let
mut
client
=
UdpOutput
::
connect
(
"localhost:25000"
)
?
;
for
_
in
interval
(
Duration
::
from_secs
(
1
))
{
let
data
=
EnvData
{
temp
:
25.0
,
hum
:
50.0
,
pressure
:
1000.0
,
};
if
let
Err
(
e
)
=
client
.send
(
data
)
{
error!
(
worker
=
self
.worker_name
(),
error
=%
e
,
"udp send error"
);
}
}
Ok
(())
}
}
// A worker to print data, received by the `UdpIn` worker
#[derive(WorkerOpts)]
#[worker_opts(name
=
"printEnv"
)]
struct
PrintEnv
{}
impl
Worker
<
Message
,
()
>
for
PrintEnv
{
fn
run
(
&
mut
self
,
context
:
&
Context
<
Message
,
()
>
)
->
WResult
{
let
hc
=
context
.hub
()
.register
(
self
.worker_name
(),
event_matches!
(
Message
::
Env
(
_
)))
?
;
for
msg
in
hc
{
let
Message
::
Env
(
data
)
=
msg
;
info!
(
worker
=
self
.worker_name
(),
data
=?
data
);
}
Ok
(())
}
}
fn
main
()
->
Result
<
(),
Box
<
dyn
std
::
error
::
Error
>>
{
// sets the simulated mode for the real-time module, do not set any thread real-time parameters
roboplc
::
thread_rt
::
set_simulated
();
// initializes a debug logger
env_logger
::
builder
()
.filter_level
(
log
::
LevelFilter
::
Info
)
.init
();
// creates a controller instance
let
mut
controller
=
Controller
::
<
Message
,
()
>
::
new
();
// spawns workers
controller
.spawn_worker
(
UdpIn
{})
?
;
controller
.spawn_worker
(
PrintEnv
{})
?
;
controller
.spawn_worker
(
UdpOut
{})
?
;
// block the main thread until the controller is in the online state
controller
.block_while_online
();
Ok
(())
}
src/controller.rs
浏览文件 @
7f0c73c2
...
@@ -13,14 +13,17 @@ use crate::{
...
@@ -13,14 +13,17 @@ use crate::{
thread_rt
::{
Builder
,
RTParams
,
Scheduling
},
thread_rt
::{
Builder
,
RTParams
,
Scheduling
},
DataDeliveryPolicy
,
Error
,
DataDeliveryPolicy
,
Error
,
};
};
use
parking_lot
::
Mutex
;
use
parking_lot
::
RwLock
;
pub
use
roboplc_derive
::
WorkerOpts
;
pub
use
roboplc_derive
::
WorkerOpts
;
use
tracing
::
error
;
pub
mod
prelude
{
pub
mod
prelude
{
pub
use
super
::{
Context
,
Controller
,
Worker
,
WorkerOptions
};
pub
use
super
::{
Context
,
Controller
,
W
Result
,
W
orker
,
WorkerOptions
};
pub
use
roboplc_derive
::
WorkerOpts
;
pub
use
roboplc_derive
::
WorkerOpts
;
}
}
pub
type
WResult
=
Result
<
(),
Box
<
dyn
std
::
error
::
Error
>>
;
const
SLEEP_SLEEP
:
Duration
=
Duration
::
from_millis
(
100
);
const
SLEEP_SLEEP
:
Duration
=
Duration
::
from_millis
(
100
);
/// Controller state beacon. Can be cloned and shared with no limitations.
/// Controller state beacon. Can be cloned and shared with no limitations.
...
@@ -89,18 +92,18 @@ impl From<i8> for StateKind {
...
@@ -89,18 +92,18 @@ impl From<i8> for StateKind {
pub
struct
Controller
<
D
,
V
>
pub
struct
Controller
<
D
,
V
>
where
where
D
:
DataDeliveryPolicy
+
Clone
+
Send
+
Sync
+
'static
,
D
:
DataDeliveryPolicy
+
Clone
+
Send
+
Sync
+
'static
,
V
:
Send
+
'static
,
V
:
Send
+
Sync
+
'static
,
{
{
supervisor
:
Supervisor
<
()
>
,
supervisor
:
Supervisor
<
()
>
,
hub
:
Hub
<
D
>
,
hub
:
Hub
<
D
>
,
state
:
State
,
state
:
State
,
variables
:
Arc
<
Mutex
<
V
>>
,
variables
:
Arc
<
RwLock
<
V
>>
,
}
}
impl
<
D
,
V
>
Controller
<
D
,
V
>
impl
<
D
,
V
>
Controller
<
D
,
V
>
where
where
D
:
DataDeliveryPolicy
+
Clone
+
Send
+
Sync
+
'static
,
D
:
DataDeliveryPolicy
+
Clone
+
Send
+
Sync
+
'static
,
V
:
Send
+
'static
,
V
:
Send
+
Sync
+
'static
,
{
{
/// Creates a new controller instance, variables MUST implement [`Default`] trait
/// Creates a new controller instance, variables MUST implement [`Default`] trait
pub
fn
new
()
->
Self
pub
fn
new
()
->
Self
...
@@ -123,7 +126,7 @@ where
...
@@ -123,7 +126,7 @@ where
supervisor
:
<
_
>
::
default
(),
supervisor
:
<
_
>
::
default
(),
hub
:
<
_
>
::
default
(),
hub
:
<
_
>
::
default
(),
state
:
State
::
new
(),
state
:
State
::
new
(),
variables
:
Arc
::
new
(
Mutex
::
new
(
variables
)),
variables
:
Arc
::
new
(
RwLock
::
new
(
variables
)),
}
}
}
}
/// Spawns a worker
/// Spawns a worker
...
@@ -146,7 +149,9 @@ where
...
@@ -146,7 +149,9 @@ where
builder
=
builder
.stack_size
(
stack_size
);
builder
=
builder
.stack_size
(
stack_size
);
}
}
self
.supervisor
.spawn
(
builder
,
move
||
{
self
.supervisor
.spawn
(
builder
,
move
||
{
worker
.run
(
&
context
);
if
let
Err
(
e
)
=
worker
.run
(
&
context
)
{
error!
(
worker
=
worker
.worker_name
(),
error
=%
e
,
"worker terminated"
);
}
})
?
;
})
?
;
Ok
(())
Ok
(())
}
}
...
@@ -194,7 +199,7 @@ where
...
@@ -194,7 +199,7 @@ where
&
self
.supervisor
&
self
.supervisor
}
}
/// Controller shared variables
/// Controller shared variables
pub
fn
variables
(
&
self
)
->
&
Arc
<
Mutex
<
V
>>
{
pub
fn
variables
(
&
self
)
->
&
Arc
<
RwLock
<
V
>>
{
&
self
.variables
&
self
.variables
}
}
}
}
...
@@ -202,7 +207,7 @@ where
...
@@ -202,7 +207,7 @@ where
impl
<
D
,
V
>
Default
for
Controller
<
D
,
V
>
impl
<
D
,
V
>
Default
for
Controller
<
D
,
V
>
where
where
D
:
DataDeliveryPolicy
+
Clone
+
Send
+
Sync
+
'static
,
D
:
DataDeliveryPolicy
+
Clone
+
Send
+
Sync
+
'static
,
V
:
Send
+
'static
+
Default
,
V
:
Send
+
Sync
+
'static
+
Default
,
{
{
fn
default
()
->
Self
{
fn
default
()
->
Self
{
Self
::
new
()
Self
::
new
()
...
@@ -218,7 +223,7 @@ where
...
@@ -218,7 +223,7 @@ where
{
{
hub
:
Hub
<
D
>
,
hub
:
Hub
<
D
>
,
state
:
State
,
state
:
State
,
variables
:
Arc
<
Mutex
<
V
>>
,
variables
:
Arc
<
RwLock
<
V
>>
,
}
}
impl
<
D
,
V
>
Context
<
D
,
V
>
impl
<
D
,
V
>
Context
<
D
,
V
>
...
@@ -231,7 +236,7 @@ where
...
@@ -231,7 +236,7 @@ where
&
self
.hub
&
self
.hub
}
}
/// Controller's shared variables (locked)
/// Controller's shared variables (locked)
pub
fn
variables
(
&
self
)
->
&
Arc
<
Mutex
<
V
>>
{
pub
fn
variables
(
&
self
)
->
&
Arc
<
RwLock
<
V
>>
{
&
self
.variables
&
self
.variables
}
}
/// Controller's state
/// Controller's state
...
@@ -253,7 +258,7 @@ pub trait Worker<D: DataDeliveryPolicy + Clone + Send + Sync + 'static, V: Send>
...
@@ -253,7 +258,7 @@ pub trait Worker<D: DataDeliveryPolicy + Clone + Send + Sync + 'static, V: Send>
Send
+
Sync
Send
+
Sync
{
{
/// The worker's main function, started by [`Controller::spawn_worker()`]
/// The worker's main function, started by [`Controller::spawn_worker()`]
fn
run
(
&
mut
self
,
context
:
&
Context
<
D
,
V
>
);
fn
run
(
&
mut
self
,
context
:
&
Context
<
D
,
V
>
)
->
WResult
;
}
}
/// The trait which MUST be implemented by all workers
/// The trait which MUST be implemented by all workers
...
...
src/io/mod.rs
浏览文件 @
7f0c73c2
...
@@ -4,6 +4,7 @@ use binrw::{BinRead, BinWrite};
...
@@ -4,6 +4,7 @@ use binrw::{BinRead, BinWrite};
use
crate
::
Result
;
use
crate
::
Result
;
pub
mod
modbus
;
pub
mod
modbus
;
pub
mod
raw_udp
;
#[allow(clippy
::
module_name_repetitions)]
#[allow(clippy
::
module_name_repetitions)]
pub
trait
IoMapping
{
pub
trait
IoMapping
{
...
...
src/io/raw_udp.rs
0 → 100644
浏览文件 @
7f0c73c2
use
binrw
::{
BinRead
,
BinWrite
};
use
std
::{
io
::
Cursor
,
marker
::
PhantomData
,
net
::{
SocketAddr
,
ToSocketAddrs
,
UdpSocket
},
};
use
crate
::{
Error
,
Result
};
pub
struct
UdpInput
<
T
>
where
T
:
for
<
'a
>
BinRead
<
Args
<
'a
>
=
()
>
,
{
server
:
UdpSocket
,
buffer
:
Vec
<
u8
>
,
_phantom
:
PhantomData
<
T
>
,
}
impl
<
T
>
UdpInput
<
T
>
where
T
:
for
<
'a
>
BinRead
<
Args
<
'a
>
=
()
>
,
{
pub
fn
bind
<
A
:
ToSocketAddrs
>
(
addr
:
A
,
buf_size
:
usize
)
->
Result
<
Self
>
{
let
server
=
UdpSocket
::
bind
(
addr
)
?
;
Ok
(
Self
{
server
,
buffer
:
vec!
[
0
;
buf_size
],
_phantom
:
PhantomData
,
})
}
}
impl
<
T
>
Iterator
for
UdpInput
<
T
>
where
T
:
for
<
'a
>
BinRead
<
Args
<
'a
>
=
()
>
,
{
type
Item
=
Result
<
T
>
;
fn
next
(
&
mut
self
)
->
Option
<
Self
::
Item
>
{
match
self
.server
.recv
(
&
mut
self
.buffer
)
{
Ok
(
size
)
=>
{
let
mut
cursor
=
Cursor
::
new
(
&
self
.buffer
[
..
size
]);
Some
(
T
::
read_le
(
&
mut
cursor
)
.map_err
(
Into
::
into
))
}
Err
(
e
)
=>
Some
(
Err
(
e
.into
())),
}
}
}
pub
struct
UdpOutput
{
socket
:
UdpSocket
,
target
:
SocketAddr
,
data_buf
:
Vec
<
u8
>
,
}
impl
UdpOutput
{
pub
fn
connect
<
A
:
ToSocketAddrs
>
(
addr
:
A
)
->
Result
<
Self
>
{
let
socket
=
UdpSocket
::
bind
((
"0.0.0.0"
,
0
))
?
;
let
target
=
addr
.to_socket_addrs
()
?
.next
()
.ok_or_else
(||
Error
::
InvalidData
(
"no target address provided"
.to_string
()))
?
;
Ok
(
Self
{
socket
,
target
,
data_buf
:
<
_
>
::
default
(),
})
}
pub
fn
send
<
T
>
(
&
mut
self
,
value
:
T
)
->
Result
<
()
>
where
T
:
for
<
'a
>
BinWrite
<
Args
<
'a
>
=
()
>
,
{
let
mut
buf
=
Cursor
::
new
(
&
mut
self
.data_buf
);
value
.write_le
(
&
mut
buf
)
?
;
self
.socket
.send_to
(
&
self
.data_buf
,
self
.target
)
?
;
Ok
(())
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论