PUS Event Manager #19
@ -1,12 +1,13 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Docs" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="doc" />
|
||||
<option name="command" value="doc --all-features" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
|
74
Cargo.lock
generated
74
Cargo.lock
generated
@ -61,6 +61,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.1"
|
||||
@ -104,9 +110,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -116,9 +122,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.22"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"num-integer",
|
||||
@ -208,9 +214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
|
||||
checksum = "d4a41a86530d0fe7f5d9ea779916b7cadd2d4f9add748b99c2c029cbbdfaf453"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@ -220,9 +226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
|
||||
checksum = "06416d667ff3e3ad2df1cd8cd8afae5da26cf9cec4d0825040f88b5ca659a2f0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@ -235,15 +241,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
|
||||
checksum = "820a9a2af1669deeef27cb271f476ffd196a2c4b6731336011e0ba63e2c7cf71"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.80"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
|
||||
checksum = "a08a6e2fcc370a089ad3b4aaf54db3b1b4cee38ddabce5896b33eb693275f470"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -278,6 +284,18 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "embed-doc-image"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af36f591236d9d822425cb6896595658fa558fcebf5ee8accac1d4b92c47166e"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
@ -296,10 +314,12 @@ dependencies = [
|
||||
"crossbeam-channel",
|
||||
"delegate 0.8.0",
|
||||
"downcast-rs",
|
||||
"embed-doc-image",
|
||||
"hashbrown",
|
||||
"heapless",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"postcard",
|
||||
"serde",
|
||||
"spacepackets",
|
||||
@ -371,9 +391,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.51"
|
||||
version = "0.1.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5a6ef98976b22b3b7f2f3a806f858cb862044cfa66805aa3ad84cb3d3b785ed"
|
||||
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
@ -410,9 +430,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.135"
|
||||
version = "0.2.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
@ -484,9 +504,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
@ -494,9 +514,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
@ -511,6 +531,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.0.2"
|
||||
@ -551,9 +577,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@ -562,9 +588,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
|
9
fsrc-core/CHANGELOG.md
Normal file
9
fsrc-core/CHANGELOG.md
Normal file
@ -0,0 +1,9 @@
|
||||
Change Log
|
||||
=======
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
@ -9,6 +9,8 @@ edition = "2021"
|
||||
delegate = "0.8"
|
||||
hashbrown = "0.12"
|
||||
heapless = "0.7"
|
||||
paste = "1.0"
|
||||
embed-doc-image = "0.1"
|
||||
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
@ -42,3 +44,8 @@ default = ["std"]
|
||||
std = ["downcast-rs/std", "alloc", "bus", "postcard/use-std", "crossbeam-channel/std"]
|
||||
alloc = []
|
||||
heapless = []
|
||||
doc-images = []
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "doc_cfg"]
|
||||
|
259
fsrc-core/images/event_man_arch.graphml
Normal file
259
fsrc-core/images/event_man_arch.graphml
Normal file
@ -0,0 +1,259 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.22-->
|
||||
<key attr.name="Description" attr.type="string" for="graph" id="d0"/>
|
||||
<key for="port" id="d1" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d2" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d3" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/>
|
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d7" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
|
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<data key="d0" xml:space="preserve"/>
|
||||
<node id="n0">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="509.9999999999999" width="768.7000000000003" x="579.3105418719211" y="304.7"/>
|
||||
<y:Fill hasColor="false" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="21.936037063598633" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="150.1282958984375" x="26.197490701913352" xml:space="preserve" y="24.234711021505348">Example Event Flow<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.46591974671274444" nodeRatioY="-0.452480958781362" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="203.0" x="814.0" y="506.6799999999999"/>
|
||||
<y:Fill color="#FFFF00" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.452094078063965" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="86.21258544921875" x="58.393707275390625" xml:space="preserve" y="21.27395296096796">Event Manager<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n2">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="82.0" x="617.6" y="413.23"/>
|
||||
<y:Fill color="#FF9900" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.120361328125" x="13.4398193359375" xml:space="preserve" y="14.547905921936035">Event
|
||||
Creator 0<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n3">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="76.55999999999995" x="988.5" y="335.62999999999994"/>
|
||||
<y:Fill color="#FF9900" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.120361328125" x="10.719819335937473" xml:space="preserve" y="14.547905921936035">Event
|
||||
Creator 2<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n4">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="72.55999999999983" x="860.6610837438426" y="335.62999999999994"/>
|
||||
<y:Fill color="#FF9900" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.120361328125" x="8.719819335937359" xml:space="preserve" y="14.547905921936035">Event
|
||||
Creator 1<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n5">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="87.27999999999997" x="1112.52" y="335.62999999999994"/>
|
||||
<y:Fill color="#FF9900" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.120361328125" x="16.079819335937373" xml:space="preserve" y="14.547905921936035">Event
|
||||
Creator 3<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n6">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="126.0" x="781.0" y="620.26"/>
|
||||
<y:Fill color="#FFCC00" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="92.78865051269531" x="16.605674743652344" xml:space="preserve" y="14.547905921936035">PUS Service 5
|
||||
Event Reporting
|
||||
<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n7">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="118.63999999999987" x="928.2" y="620.26"/>
|
||||
<y:Fill color="#FFCC00" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="84.08859252929688" x="17.2757037353515" xml:space="preserve" y="14.547905921936035">PUS Service 19
|
||||
Event Action<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n8">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="60.0" width="87.27999999999997" x="792.1260377358491" y="733.8400000000001"/>
|
||||
<y:Fill color="#FFCC99" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="59.932403564453125" x="13.673798217773424" xml:space="preserve" y="14.547905921936035">Telemetry
|
||||
Sink<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n9">
|
||||
<data key="d6">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="170.79999999999995" width="210.80000000000018" x="1076.84" y="601.88"/>
|
||||
<y:Fill hasColor="false" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="143.6875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="181.591796875" x="8.373079774614325" xml:space="preserve" y="7.444138124199753">Subscriptions
|
||||
|
||||
1. Event Creator 0 subscribes
|
||||
for event 0
|
||||
2. Event Creator 1 subscribes
|
||||
for event group 2
|
||||
3. PUS Service 5 handler
|
||||
subscribes for all events
|
||||
4. PUS Service 19 handler
|
||||
subscribes for all events<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.4602795077105583" nodeRatioY="-0.45641605313700395" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<edge id="e0" source="n4" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="8.058916256157545" sy="0.0" tx="-10.5" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="53.92036437988281" x="8.639817810058275" xml:space="preserve" y="29.00100609374465">event 1
|
||||
(group 1)<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="35.59999999999969" distanceToCenter="true" position="left" ratio="0.34252387409930674" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e1" source="n2" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="11.93999999999994" tx="-83.5" ty="0.0">
|
||||
<y:Point x="832.0" y="455.16999999999996"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="53.92036437988281" x="25.334655000000453" xml:space="preserve" y="-40.972107505798476">event 0
|
||||
(group 0)<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="25.520000000000095" distanceToCenter="true" position="left" ratio="0.20267159489379444" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e2" source="n3" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-23.719999999999914" sy="5.5" tx="87.56000000000006" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="53.92036437988281" x="5.6761352539062955" xml:space="preserve" y="27.551854405966765">event 2
|
||||
(group 3)<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="5.676132812499983" distanceToCenter="false" position="left" ratio="0.3219761157957032" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n5" target="n1">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-6.275467980295616" sy="0.0" tx="57.5" ty="8.5">
|
||||
<y:Point x="1149.8845320197042" y="545.1799999999998"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="97.38468933105469" x="26.667665869801795" xml:space="preserve" y="43.287014528669715">event 3 (group 2)
|
||||
event 4 (group 2)<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="75.3599999999999" distanceToCenter="true" position="left" ratio="0.2967848459873102" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e4" source="n1" target="n6">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-65.0" sy="0.0" tx="6.5" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.452094078063965" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.16456604003906" x="-98.78228302001958" xml:space="preserve" y="16.63042580701972"><<all events>><y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="57.20000000000004" distanceToCenter="true" position="right" ratio="0.4441995640590947" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e5" source="n1" target="n7">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="42.660000000000196" sy="0.0" tx="-29.359999999999786" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.452094078063965" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="83.16456604003906" x="20.4177438354493" xml:space="preserve" y="17.885881494816203"><<all events>><y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="62.0" distanceToCenter="true" position="left" ratio="0.492249939452652" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e6" source="n1" target="n2">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
|
||||
<y:Point x="658.6" y="536.6799999999998"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="44.69230651855469" x="-131.99129340961497" xml:space="preserve" y="-45.45208675384538">event 1
|
||||
event 2<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.6426904695623505" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e7" source="n1" target="n4">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="-35.69940886699487" sy="0.0" tx="-17.140492610837327" ty="1.5"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.452094078063965" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="46.14430236816406" x="-54.352158195608126" xml:space="preserve" y="-79.29459128622307">group 2<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="31.279999999999973" distanceToCenter="true" position="left" ratio="0.6800790648728832" segment="-1"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e8" source="n6" target="n8">
|
||||
<data key="d10">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="8.233962264150945" ty="-21.42352238805968"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Ubuntu" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="30.90418815612793" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="87.40060424804688" x="-100.50030212402339" xml:space="preserve" y="11.337896156311103">enabled Events
|
||||
as PUS 5 TM<y:LabelModel><y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/></y:LabelModel><y:ModelParameter><y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="56.79999999999995" distanceToCenter="true" position="right" ratio="0.5" segment="0"/></y:ModelParameter><y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d7">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
BIN
fsrc-core/images/event_man_arch.png
Normal file
BIN
fsrc-core/images/event_man_arch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
@ -1,226 +1,551 @@
|
||||
//! [Event][crate::events::Event] management and forwarding
|
||||
use crate::events::{Event, EventProvider, EventSmall};
|
||||
//! Event management and forwarding
|
||||
//!
|
||||
//! This module provides components to perform event routing. The most important component for this
|
||||
//! task is the [EventManager]. It uses a map of event listeners and uses a dynamic [EventReceiver]
|
||||
//! instance to receive all events and then route them to event subscribers where appropriate.
|
||||
#![cfg_attr(feature = "doc-images",
|
||||
cfg_attr(all(),
|
||||
doc = ::embed_doc_image::embed_image!("event_man_arch", "images/event_man_arch.png"
|
||||
)))]
|
||||
#![cfg_attr(
|
||||
not(feature = "doc-images"),
|
||||
doc = "**Doc images not enabled**. Compile with feature `doc-images` and Rust version >= 1.54 \
|
||||
to enable."
|
||||
)]
|
||||
//! One common use case for satellite systems is to offer a light-weight publish-subscribe mechanism
|
||||
//! and IPC mechanism for software and hardware events which are also packaged as telemetry or can
|
||||
//! trigger a system response.
|
||||
//!
|
||||
//! The following graph shows how the event flow for such a setup could look like:
|
||||
//!
|
||||
//! ![Event flow][event_man_arch]
|
||||
//!
|
||||
//! The event manager has a listener table abstracted by the [ListenerTable], which maps
|
||||
//! listener groups identified by [ListenerKey]s to a [sender ID][SenderId].
|
||||
//! It also contains a sender table abstracted by the [SenderTable] which maps these sender IDs
|
||||
//! to a concrete [SendEventProvider]. A simple approach would be to use one send event provider
|
||||
//! for each OBSW thread and then subscribe for all interesting events for a particular thread
|
||||
//! using the send event provider ID.
|
||||
//!
|
||||
//! This can be done with the [EventManager] like this:
|
||||
//!
|
||||
//! 1. Provide a concrete [EventReceiver] implementation. This abstraction allow to use different
|
||||
//! message queue backends. A straightforward implementation where dynamic memory allocation is
|
||||
//! not a big concern could use [std::sync::mpsc::channel] to do this and is provided in
|
||||
//! form of the [MpscEventSendProvider].
|
||||
//! 2. To set up event creators, create channel pairs using some message queue implementation.
|
||||
//! Each event creator gets a (cloned) sender component which allows it to send events to the
|
||||
//! manager.
|
||||
//! 3. The event manager receives the receiver component so all events are routed to the
|
||||
//! manager.
|
||||
//! 4. Create the [send event providers][SendEventProvider]s which allow routing events to
|
||||
//! subscribers.
|
||||
//! Use can now use their [sender IDs][SendEventProvider::id] to subscribe for event groups,
|
||||
//! for example by using the [EventManager::subscribe_single] method.
|
||||
//! 5. Add the send provider as well using the [EventManager::add_sender] call so the event
|
||||
//! manager can route listener groups to a the send provider.
|
||||
//!
|
||||
//! Some components like a PUS Event Service or PUS Event Action Service might require all
|
||||
//! events to package them as telemetry or start actions where applicable.
|
||||
//! Other components might only be interested in certain events. For example, a thermal system
|
||||
//! handler might only be interested in temperature events generated by a thermal sensor component.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! You can check [integration test](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/event_man_table_impl/fsrc-core/tests/pus_events.rs)
|
||||
//! for a concrete example using multi-threading where events are routed to
|
||||
//! different threads.
|
||||
use crate::events::{EventU16, EventU32, GenericEvent, LargestEventRaw, LargestGroupIdRaw};
|
||||
use crate::params::{Params, ParamsHeapless};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::slice::Iter;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
enum ListenerType {
|
||||
Single(u32),
|
||||
Group(u16),
|
||||
#[cfg(feature = "std")]
|
||||
pub use stdmod::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
pub enum ListenerKey {
|
||||
Single(LargestEventRaw),
|
||||
Group(LargestGroupIdRaw),
|
||||
All,
|
||||
}
|
||||
|
||||
pub trait EventListener<Provider: EventProvider = Event> {
|
||||
pub type EventWithHeaplessAuxData<Event> = (Event, Option<ParamsHeapless>);
|
||||
pub type EventU32WithHeaplessAuxData = EventWithHeaplessAuxData<EventU32>;
|
||||
pub type EventU16WithHeaplessAuxData = EventWithHeaplessAuxData<EventU16>;
|
||||
|
||||
pub type EventWithAuxData<Event> = (Event, Option<Params>);
|
||||
pub type EventU32WithAuxData = EventWithAuxData<EventU32>;
|
||||
pub type EventU16WithAuxData = EventWithAuxData<EventU16>;
|
||||
|
||||
pub type SenderId = u32;
|
||||
|
||||
pub trait SendEventProvider<Provider: GenericEvent, AuxDataProvider = Params> {
|
||||
type Error;
|
||||
|
||||
fn id(&self) -> u32;
|
||||
fn send_to(&mut self, event: Provider) -> Result<(), Self::Error>;
|
||||
fn id(&self) -> SenderId;
|
||||
fn send_no_data(&mut self, event: Provider) -> Result<(), Self::Error> {
|
||||
self.send(event, None)
|
||||
}
|
||||
fn send(
|
||||
&mut self,
|
||||
event: Provider,
|
||||
aux_data: Option<AuxDataProvider>,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
struct Listener<E, Provider: EventProvider = Event> {
|
||||
ltype: ListenerType,
|
||||
dest: Box<dyn EventListener<Provider, Error = E>>,
|
||||
/// Generic abstraction for an event receiver.
|
||||
pub trait EventReceiver<Event: GenericEvent, AuxDataProvider = Params> {
|
||||
/// This function has to be provided by any event receiver. A receive call may or may not return
|
||||
/// an event.
|
||||
///
|
||||
/// To allow returning arbitrary additional auxiliary data, a mutable slice is passed to the
|
||||
/// [Self::receive] call as well. Receivers can write data to this slice, but care must be taken
|
||||
/// to avoid panics due to size missmatches or out of bound writes.
|
||||
fn receive(&mut self) -> Option<(Event, Option<AuxDataProvider>)>;
|
||||
}
|
||||
|
||||
pub trait ReceivesAllEvent<Provider: EventProvider = Event> {
|
||||
fn receive(&mut self) -> Option<Provider>;
|
||||
pub trait ListenerTable {
|
||||
fn get_listeners(&self) -> Vec<ListenerKey>;
|
||||
fn contains_listener(&self, key: &ListenerKey) -> bool;
|
||||
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<SenderId>>;
|
||||
fn add_listener(&mut self, key: ListenerKey, sender_id: SenderId) -> bool;
|
||||
fn remove_duplicates(&mut self, key: &ListenerKey);
|
||||
}
|
||||
|
||||
pub struct EventManager<E, Provider: EventProvider = Event> {
|
||||
listeners: HashMap<ListenerType, Vec<Listener<E, Provider>>>,
|
||||
event_receiver: Box<dyn ReceivesAllEvent<Provider>>,
|
||||
pub trait SenderTable<SendProviderError, Event: GenericEvent = EventU32, AuxDataProvider = Params> {
|
||||
fn contains_send_event_provider(&self, id: &SenderId) -> bool;
|
||||
fn get_send_event_provider(
|
||||
&mut self,
|
||||
id: &SenderId,
|
||||
) -> Option<&mut Box<dyn SendEventProvider<Event, AuxDataProvider, Error = SendProviderError>>>;
|
||||
fn add_send_event_provider(
|
||||
&mut self,
|
||||
send_provider: Box<
|
||||
dyn SendEventProvider<Event, AuxDataProvider, Error = SendProviderError>,
|
||||
>,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
pub enum HandlerResult<Provider: EventProvider = Event> {
|
||||
/// Generic event manager implementation.
|
||||
///
|
||||
/// # Generics
|
||||
///
|
||||
/// * `SendProviderError`: [SendEventProvider] error type
|
||||
/// * `Event`: Concrete event provider, currently either [EventU32] or [EventU16]
|
||||
/// * `AuxDataProvider`: Concrete auxiliary data provider, currently either [Params] or
|
||||
/// [ParamsHeapless]
|
||||
pub struct EventManager<SendProviderError, Event: GenericEvent = EventU32, AuxDataProvider = Params>
|
||||
{
|
||||
listener_table: Box<dyn ListenerTable>,
|
||||
sender_table: Box<dyn SenderTable<SendProviderError, Event, AuxDataProvider>>,
|
||||
event_receiver: Box<dyn EventReceiver<Event, AuxDataProvider>>,
|
||||
}
|
||||
|
||||
/// Safety: It is safe to implement [Send] because all fields in the [EventManager] are [Send]
|
||||
/// as well
|
||||
#[cfg(feature = "std")]
|
||||
unsafe impl<E, Event: GenericEvent + Send, AuxDataProvider: Send> Send
|
||||
for EventManager<E, Event, AuxDataProvider>
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type EventManagerWithMpscQueue<Event, AuxDataProvider> = EventManager<
|
||||
std::sync::mpsc::SendError<(Event, Option<AuxDataProvider>)>,
|
||||
Event,
|
||||
AuxDataProvider,
|
||||
>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventRoutingResult<Event: GenericEvent, AuxDataProvider> {
|
||||
/// No event was received
|
||||
Empty,
|
||||
Handled(u32, Provider),
|
||||
/// An event was received and routed.
|
||||
/// The first tuple entry will contain the number of recipients.
|
||||
Handled(u32, Event, Option<AuxDataProvider>),
|
||||
}
|
||||
|
||||
impl<E> EventManager<E, Event> {
|
||||
pub fn new(event_receiver: Box<dyn ReceivesAllEvent<Event>>) -> Self {
|
||||
#[derive(Debug)]
|
||||
pub enum EventRoutingError<E> {
|
||||
SendError(E),
|
||||
NoSendersForKey(ListenerKey),
|
||||
NoSenderForId(SenderId),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventRoutingErrorsWithResult<Event: GenericEvent, AuxDataProvider, E> {
|
||||
pub result: EventRoutingResult<Event, AuxDataProvider>,
|
||||
pub errors: [Option<EventRoutingError<E>>; 3],
|
||||
}
|
||||
|
||||
impl<E, Event: GenericEvent + Copy> EventManager<E, Event> {
|
||||
pub fn remove_duplicates(&mut self, key: &ListenerKey) {
|
||||
self.listener_table.remove_duplicates(key)
|
||||
}
|
||||
|
||||
/// Subscribe for a unique event.
|
||||
pub fn subscribe_single(&mut self, event: &Event, sender_id: SenderId) {
|
||||
self.update_listeners(ListenerKey::Single(event.raw_as_largest_type()), sender_id);
|
||||
}
|
||||
|
||||
/// Subscribe for an event group.
|
||||
pub fn subscribe_group(&mut self, group_id: LargestGroupIdRaw, sender_id: SenderId) {
|
||||
self.update_listeners(ListenerKey::Group(group_id), sender_id);
|
||||
}
|
||||
|
||||
/// Subscribe for all events received by the manager.
|
||||
///
|
||||
/// For example, this can be useful for a handler component which sends every event as
|
||||
/// a telemetry packet.
|
||||
pub fn subscribe_all(&mut self, sender_id: SenderId) {
|
||||
self.update_listeners(ListenerKey::All, sender_id);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: 'static, Event: GenericEvent + Copy + 'static, AuxDataProvider: Clone + 'static>
|
||||
EventManager<E, Event, AuxDataProvider>
|
||||
{
|
||||
pub fn new(event_receiver: Box<dyn EventReceiver<Event, AuxDataProvider>>) -> Self {
|
||||
let listener_table = Box::new(DefaultListenerTableProvider::default());
|
||||
let sender_table =
|
||||
Box::new(DefaultSenderTableProvider::<E, Event, AuxDataProvider>::default());
|
||||
Self::new_custom_tables(listener_table, sender_table, event_receiver)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, Event: GenericEvent + Copy, AuxDataProvider: Clone>
|
||||
EventManager<E, Event, AuxDataProvider>
|
||||
{
|
||||
pub fn new_custom_tables(
|
||||
listener_table: Box<dyn ListenerTable>,
|
||||
sender_table: Box<dyn SenderTable<E, Event, AuxDataProvider>>,
|
||||
event_receiver: Box<dyn EventReceiver<Event, AuxDataProvider>>,
|
||||
) -> Self {
|
||||
EventManager {
|
||||
listeners: HashMap::new(),
|
||||
listener_table,
|
||||
sender_table,
|
||||
event_receiver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> EventManager<E, Event> {
|
||||
pub fn subscribe_single(
|
||||
pub fn add_sender(
|
||||
&mut self,
|
||||
event: Event,
|
||||
dest: impl EventListener<Event, Error = E> + 'static,
|
||||
send_provider: impl SendEventProvider<Event, AuxDataProvider, Error = E> + 'static,
|
||||
) {
|
||||
self.update_listeners(ListenerType::Single(event.raw_as_largest_type()), dest);
|
||||
if !self
|
||||
.sender_table
|
||||
.contains_send_event_provider(&send_provider.id())
|
||||
{
|
||||
self.sender_table
|
||||
.add_send_event_provider(Box::new(send_provider));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe_group(
|
||||
fn update_listeners(&mut self, key: ListenerKey, sender_id: SenderId) {
|
||||
self.listener_table.add_listener(key, sender_id);
|
||||
}
|
||||
|
||||
/// This function will use the cached event receiver and try to receive one event.
|
||||
/// If an event was received, it will try to route that event to all subscribed event listeners.
|
||||
/// If this works without any issues, the [EventRoutingResult] will contain context information
|
||||
/// about the routed event.
|
||||
///
|
||||
/// This function will track up to 3 errors returned as part of the
|
||||
/// [EventRoutingErrorsWithResult] error struct.
|
||||
pub fn try_event_handling(
|
||||
&mut self,
|
||||
group_id: <Event as EventProvider>::GroupId,
|
||||
dest: impl EventListener<Event, Error = E> + 'static,
|
||||
) {
|
||||
self.update_listeners(ListenerType::Group(group_id), dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> EventManager<E, EventSmall> {
|
||||
pub fn subscribe_single(
|
||||
&mut self,
|
||||
event: EventSmall,
|
||||
dest: impl EventListener<EventSmall, Error = E> + 'static,
|
||||
) {
|
||||
self.update_listeners(ListenerType::Single(event.raw_as_largest_type()), dest);
|
||||
}
|
||||
|
||||
pub fn subscribe_group(
|
||||
&mut self,
|
||||
group_id: <EventSmall as EventProvider>::GroupId,
|
||||
dest: impl EventListener<EventSmall, Error = E> + 'static,
|
||||
) {
|
||||
self.update_listeners(ListenerType::Group(group_id.into()), dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, Provider: EventProvider> EventManager<E, Provider> {
|
||||
fn update_listeners(
|
||||
&mut self,
|
||||
key: ListenerType,
|
||||
dest: impl EventListener<Provider, Error = E> + 'static,
|
||||
) {
|
||||
if !self.listeners.contains_key(&key) {
|
||||
self.listeners.insert(
|
||||
key,
|
||||
vec![Listener {
|
||||
ltype: key,
|
||||
dest: Box::new(dest),
|
||||
}],
|
||||
);
|
||||
} else {
|
||||
let vec = self.listeners.get_mut(&key).unwrap();
|
||||
// To prevent double insertions
|
||||
for entry in vec.iter() {
|
||||
if entry.ltype == key && entry.dest.id() == dest.id() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
vec.push(Listener {
|
||||
ltype: key,
|
||||
dest: Box::new(dest),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_event_handling(&mut self) -> Result<HandlerResult<Provider>, E> {
|
||||
let mut err_status = None;
|
||||
) -> Result<
|
||||
EventRoutingResult<Event, AuxDataProvider>,
|
||||
EventRoutingErrorsWithResult<Event, AuxDataProvider, E>,
|
||||
> {
|
||||
let mut err_idx = 0;
|
||||
let mut err_slice = [None, None, None];
|
||||
let mut num_recipients = 0;
|
||||
let mut send_handler = |event: Provider, llist: &mut Vec<Listener<E, Provider>>| {
|
||||
for listener in llist.iter_mut() {
|
||||
if let Err(e) = listener.dest.send_to(event) {
|
||||
err_status = Some(Err(e));
|
||||
let mut add_error = |error: EventRoutingError<E>| {
|
||||
if err_idx < 3 {
|
||||
err_slice[err_idx] = Some(error);
|
||||
err_idx += 1;
|
||||
}
|
||||
};
|
||||
let mut send_handler =
|
||||
|key: &ListenerKey, event: Event, aux_data: &Option<AuxDataProvider>| {
|
||||
if self.listener_table.contains_listener(key) {
|
||||
if let Some(ids) = self.listener_table.get_listener_ids(key) {
|
||||
for id in ids {
|
||||
if let Some(sender) = self.sender_table.get_send_event_provider(id) {
|
||||
if let Err(e) = sender.send(event, aux_data.clone()) {
|
||||
add_error(EventRoutingError::SendError(e));
|
||||
} else {
|
||||
num_recipients += 1;
|
||||
}
|
||||
} else {
|
||||
add_error(EventRoutingError::NoSenderForId(*id));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
add_error(EventRoutingError::NoSendersForKey(*key));
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(event) = self.event_receiver.receive() {
|
||||
let single_key = ListenerType::Single(event.raw_as_largest_type());
|
||||
if self.listeners.contains_key(&single_key) {
|
||||
send_handler(event, self.listeners.get_mut(&single_key).unwrap());
|
||||
if let Some((event, aux_data)) = self.event_receiver.receive() {
|
||||
let single_key = ListenerKey::Single(event.raw_as_largest_type());
|
||||
send_handler(&single_key, event, &aux_data);
|
||||
let group_key = ListenerKey::Group(event.group_id_as_largest_type());
|
||||
send_handler(&group_key, event, &aux_data);
|
||||
send_handler(&ListenerKey::All, event, &aux_data);
|
||||
if err_idx > 0 {
|
||||
return Err(EventRoutingErrorsWithResult {
|
||||
result: EventRoutingResult::Handled(num_recipients, event, aux_data),
|
||||
errors: err_slice,
|
||||
});
|
||||
}
|
||||
let group_key = ListenerType::Group(event.group_id_as_largest_type());
|
||||
if self.listeners.contains_key(&group_key) {
|
||||
send_handler(event, self.listeners.get_mut(&group_key).unwrap());
|
||||
return Ok(EventRoutingResult::Handled(num_recipients, event, aux_data));
|
||||
}
|
||||
if let Some(err) = err_status {
|
||||
return err;
|
||||
}
|
||||
return Ok(HandlerResult::Handled(num_recipients, event));
|
||||
}
|
||||
Ok(HandlerResult::Empty)
|
||||
Ok(EventRoutingResult::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{EventListener, HandlerResult, ReceivesAllEvent};
|
||||
use crate::event_man::EventManager;
|
||||
use crate::events::{Event, EventProvider, Severity};
|
||||
use alloc::boxed::Box;
|
||||
use std::sync::mpsc::{channel, Receiver, SendError, Sender};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
#[derive(Default)]
|
||||
pub struct DefaultListenerTableProvider {
|
||||
listeners: HashMap<ListenerKey, Vec<SenderId>>,
|
||||
}
|
||||
|
||||
struct EventReceiver {
|
||||
mpsc_receiver: Receiver<Event>,
|
||||
}
|
||||
impl ReceivesAllEvent<Event> for EventReceiver {
|
||||
fn receive(&mut self) -> Option<Event> {
|
||||
self.mpsc_receiver.try_recv().ok()
|
||||
pub struct DefaultSenderTableProvider<
|
||||
SendProviderError,
|
||||
Event: GenericEvent = EventU32,
|
||||
AuxDataProvider = Params,
|
||||
> {
|
||||
senders: HashMap<
|
||||
SenderId,
|
||||
Box<dyn SendEventProvider<Event, AuxDataProvider, Error = SendProviderError>>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl<SendProviderError, Event: GenericEvent, AuxDataProvider> Default
|
||||
for DefaultSenderTableProvider<SendProviderError, Event, AuxDataProvider>
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
senders: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ListenerTable for DefaultListenerTableProvider {
|
||||
fn get_listeners(&self) -> Vec<ListenerKey> {
|
||||
let mut key_list = Vec::new();
|
||||
for key in self.listeners.keys() {
|
||||
key_list.push(*key);
|
||||
}
|
||||
key_list
|
||||
}
|
||||
|
||||
fn contains_listener(&self, key: &ListenerKey) -> bool {
|
||||
self.listeners.contains_key(key)
|
||||
}
|
||||
|
||||
fn get_listener_ids(&self, key: &ListenerKey) -> Option<Iter<SenderId>> {
|
||||
self.listeners.get(key).map(|vec| vec.iter())
|
||||
}
|
||||
|
||||
fn add_listener(&mut self, key: ListenerKey, sender_id: SenderId) -> bool {
|
||||
if let Some(existing_list) = self.listeners.get_mut(&key) {
|
||||
existing_list.push(sender_id);
|
||||
} else {
|
||||
let new_list = vec![sender_id];
|
||||
self.listeners.insert(key, new_list);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn remove_duplicates(&mut self, key: &ListenerKey) {
|
||||
if let Some(list) = self.listeners.get_mut(key) {
|
||||
list.sort_unstable();
|
||||
list.dedup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SendProviderError, Event: GenericEvent, AuxDataProvider>
|
||||
SenderTable<SendProviderError, Event, AuxDataProvider>
|
||||
for DefaultSenderTableProvider<SendProviderError, Event, AuxDataProvider>
|
||||
{
|
||||
fn contains_send_event_provider(&self, id: &SenderId) -> bool {
|
||||
self.senders.contains_key(id)
|
||||
}
|
||||
|
||||
fn get_send_event_provider(
|
||||
&mut self,
|
||||
id: &SenderId,
|
||||
) -> Option<&mut Box<dyn SendEventProvider<Event, AuxDataProvider, Error = SendProviderError>>>
|
||||
{
|
||||
self.senders.get_mut(id).filter(|sender| sender.id() == *id)
|
||||
}
|
||||
|
||||
fn add_send_event_provider(
|
||||
&mut self,
|
||||
send_provider: Box<
|
||||
dyn SendEventProvider<Event, AuxDataProvider, Error = SendProviderError>,
|
||||
>,
|
||||
) -> bool {
|
||||
let id = send_provider.id();
|
||||
if self.senders.contains_key(&id) {
|
||||
return false;
|
||||
}
|
||||
self.senders.insert(id, send_provider).is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod stdmod {
|
||||
use super::*;
|
||||
use crate::event_man::{EventReceiver, EventWithAuxData};
|
||||
use crate::events::{EventU16, EventU32, GenericEvent};
|
||||
use crate::params::Params;
|
||||
use std::sync::mpsc::{Receiver, SendError, Sender};
|
||||
|
||||
pub struct MpscEventReceiver<Event: GenericEvent + Send = EventU32> {
|
||||
mpsc_receiver: Receiver<(Event, Option<Params>)>,
|
||||
}
|
||||
|
||||
impl<Event: GenericEvent + Send> MpscEventReceiver<Event> {
|
||||
pub fn new(receiver: Receiver<(Event, Option<Params>)>) -> Self {
|
||||
Self {
|
||||
mpsc_receiver: receiver,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<Event: GenericEvent + Send> EventReceiver<Event> for MpscEventReceiver<Event> {
|
||||
fn receive(&mut self) -> Option<EventWithAuxData<Event>> {
|
||||
if let Ok(event_and_data) = self.mpsc_receiver.try_recv() {
|
||||
return Some(event_and_data);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub type MpscEventU32Receiver = MpscEventReceiver<EventU32>;
|
||||
pub type MpscEventU16Receiver = MpscEventReceiver<EventU16>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MpscEventSenderQueue {
|
||||
pub struct MpscEventSendProvider<Event: GenericEvent + Send> {
|
||||
id: u32,
|
||||
mpsc_sender: Sender<Event>,
|
||||
sender: Sender<(Event, Option<Params>)>,
|
||||
}
|
||||
|
||||
impl EventListener<Event> for MpscEventSenderQueue {
|
||||
type Error = SendError<Event>;
|
||||
impl<Event: GenericEvent + Send> MpscEventSendProvider<Event> {
|
||||
pub fn new(id: u32, sender: Sender<(Event, Option<Params>)>) -> Self {
|
||||
Self { id, sender }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Event: GenericEvent + Send> SendEventProvider<Event> for MpscEventSendProvider<Event> {
|
||||
type Error = SendError<(Event, Option<Params>)>;
|
||||
|
||||
fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
fn send_to(&mut self, event: Event) -> Result<(), Self::Error> {
|
||||
self.mpsc_sender.send(event)
|
||||
fn send(&mut self, event: Event, aux_data: Option<Params>) -> Result<(), Self::Error> {
|
||||
self.sender.send((event, aux_data))
|
||||
}
|
||||
}
|
||||
|
||||
fn check_next_event(expected: Event, receiver: &Receiver<Event>) {
|
||||
for _ in 0..5 {
|
||||
pub type MpscEventU32SendProvider = MpscEventSendProvider<EventU32>;
|
||||
pub type MpscEventU16SendProvider = MpscEventSendProvider<EventU16>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::event_man::EventManager;
|
||||
use crate::events::{EventU32, GenericEvent, Severity};
|
||||
use crate::params::ParamsRaw;
|
||||
use alloc::boxed::Box;
|
||||
use std::format;
|
||||
use std::sync::mpsc::{channel, Receiver, SendError, Sender};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MpscEventSenderQueue {
|
||||
id: u32,
|
||||
mpsc_sender: Sender<EventU32WithAuxData>,
|
||||
}
|
||||
|
||||
impl MpscEventSenderQueue {
|
||||
fn new(id: u32, mpsc_sender: Sender<EventU32WithAuxData>) -> Self {
|
||||
Self { id, mpsc_sender }
|
||||
}
|
||||
}
|
||||
|
||||
impl SendEventProvider<EventU32> for MpscEventSenderQueue {
|
||||
type Error = SendError<EventU32WithAuxData>;
|
||||
|
||||
fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
fn send(&mut self, event: EventU32, aux_data: Option<Params>) -> Result<(), Self::Error> {
|
||||
self.mpsc_sender.send((event, aux_data))
|
||||
}
|
||||
}
|
||||
|
||||
fn check_next_event(
|
||||
expected: EventU32,
|
||||
receiver: &Receiver<EventU32WithAuxData>,
|
||||
) -> Option<Params> {
|
||||
if let Ok(event) = receiver.try_recv() {
|
||||
assert_eq!(event, expected);
|
||||
break;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
assert_eq!(event.0, expected);
|
||||
return event.1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn check_handled_event(res: HandlerResult, expected: Event, expected_num_sent: u32) {
|
||||
assert!(matches!(res, HandlerResult::Handled { .. }));
|
||||
if let HandlerResult::Handled(num_recipients, event) = res {
|
||||
fn check_handled_event(
|
||||
res: EventRoutingResult<EventU32, Params>,
|
||||
expected: EventU32,
|
||||
expected_num_sent: u32,
|
||||
) {
|
||||
assert!(matches!(res, EventRoutingResult::Handled { .. }));
|
||||
if let EventRoutingResult::Handled(num_recipients, event, _aux_data) = res {
|
||||
assert_eq!(event, expected);
|
||||
assert_eq!(num_recipients, expected_num_sent);
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_event_man() -> (
|
||||
Sender<EventU32WithAuxData>,
|
||||
EventManager<SendError<EventU32WithAuxData>>,
|
||||
) {
|
||||
let (event_sender, manager_queue) = channel();
|
||||
let event_man_receiver = MpscEventReceiver::new(manager_queue);
|
||||
(
|
||||
event_sender,
|
||||
EventManager::new(Box::new(event_man_receiver)),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let (event_sender, manager_queue) = channel();
|
||||
let event_man_receiver = EventReceiver {
|
||||
mpsc_receiver: manager_queue,
|
||||
};
|
||||
let mut event_man: EventManager<SendError<Event>, Event> =
|
||||
EventManager::new(Box::new(event_man_receiver));
|
||||
let event_grp_0 = Event::new(Severity::INFO, 0, 0).unwrap();
|
||||
let event_grp_1_0 = Event::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let (event_sender, mut event_man) = generic_event_man();
|
||||
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap();
|
||||
let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let (single_event_sender, single_event_receiver) = channel();
|
||||
let single_event_listener = MpscEventSenderQueue {
|
||||
id: 0,
|
||||
mpsc_sender: single_event_sender,
|
||||
};
|
||||
event_man.subscribe_single(event_grp_0, single_event_listener);
|
||||
let single_event_listener = MpscEventSenderQueue::new(0, single_event_sender);
|
||||
event_man.subscribe_single(&event_grp_0, single_event_listener.id());
|
||||
event_man.add_sender(single_event_listener);
|
||||
let (group_event_sender_0, group_event_receiver_0) = channel();
|
||||
let group_event_listener = MpscEventSenderQueue {
|
||||
id: 1,
|
||||
mpsc_sender: group_event_sender_0,
|
||||
};
|
||||
event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener);
|
||||
event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener.id());
|
||||
event_man.add_sender(group_event_listener);
|
||||
|
||||
// Test event with one listener
|
||||
event_sender
|
||||
.send(event_grp_0)
|
||||
.send((event_grp_0, None))
|
||||
.expect("Sending single error failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
@ -229,7 +554,7 @@ mod tests {
|
||||
|
||||
// Test event which is sent to all group listeners
|
||||
event_sender
|
||||
.send(event_grp_1_0)
|
||||
.send((event_grp_1_0, None))
|
||||
.expect("Sending group error failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
@ -237,35 +562,56 @@ mod tests {
|
||||
check_next_event(event_grp_1_0, &group_event_receiver_0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_basic_aux_data() {
|
||||
let (event_sender, mut event_man) = generic_event_man();
|
||||
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap();
|
||||
let (single_event_sender, single_event_receiver) = channel();
|
||||
let single_event_listener = MpscEventSenderQueue::new(0, single_event_sender);
|
||||
event_man.subscribe_single(&event_grp_0, single_event_listener.id());
|
||||
event_man.add_sender(single_event_listener);
|
||||
event_sender
|
||||
.send((event_grp_0, Some(Params::Heapless((2_u32, 3_u32).into()))))
|
||||
.expect("Sending group error failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
check_handled_event(res.unwrap(), event_grp_0, 1);
|
||||
let aux = check_next_event(event_grp_0, &single_event_receiver);
|
||||
assert!(aux.is_some());
|
||||
let aux = aux.unwrap();
|
||||
if let Params::Heapless(ParamsHeapless::Raw(ParamsRaw::U32Pair(pair))) = aux {
|
||||
assert_eq!(pair.0, 2);
|
||||
assert_eq!(pair.1, 3);
|
||||
} else {
|
||||
panic!("{}", format!("Unexpected auxiliary value type {:?}", aux));
|
||||
}
|
||||
}
|
||||
|
||||
/// Test listening for multiple groups
|
||||
#[test]
|
||||
fn test_multi_group() {
|
||||
let (event_sender, manager_queue) = channel();
|
||||
let event_man_receiver = EventReceiver {
|
||||
mpsc_receiver: manager_queue,
|
||||
};
|
||||
let mut event_man: EventManager<SendError<Event>, Event> =
|
||||
EventManager::new(Box::new(event_man_receiver));
|
||||
let (event_sender, mut event_man) = generic_event_man();
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
let hres = res.unwrap();
|
||||
assert!(matches!(hres, HandlerResult::Empty));
|
||||
assert!(matches!(hres, EventRoutingResult::Empty));
|
||||
|
||||
let event_grp_0 = Event::new(Severity::INFO, 0, 0).unwrap();
|
||||
let event_grp_1_0 = Event::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let event_grp_0 = EventU32::new(Severity::INFO, 0, 0).unwrap();
|
||||
let event_grp_1_0 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let (event_grp_0_sender, event_grp_0_receiver) = channel();
|
||||
let event_grp_0_and_1_listener = MpscEventSenderQueue {
|
||||
id: 0,
|
||||
mpsc_sender: event_grp_0_sender,
|
||||
};
|
||||
event_man.subscribe_group(event_grp_0.group_id(), event_grp_0_and_1_listener.clone());
|
||||
event_man.subscribe_group(event_grp_1_0.group_id(), event_grp_0_and_1_listener);
|
||||
event_man.subscribe_group(event_grp_0.group_id(), event_grp_0_and_1_listener.id());
|
||||
event_man.subscribe_group(event_grp_1_0.group_id(), event_grp_0_and_1_listener.id());
|
||||
event_man.add_sender(event_grp_0_and_1_listener);
|
||||
|
||||
event_sender
|
||||
.send(event_grp_0)
|
||||
.send((event_grp_0, None))
|
||||
.expect("Sending Event Group 0 failed");
|
||||
event_sender
|
||||
.send(event_grp_1_0)
|
||||
.send((event_grp_1_0, None))
|
||||
.expect("Sendign Event Group 1 failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
@ -282,14 +628,9 @@ mod tests {
|
||||
/// to both group and single events from one listener
|
||||
#[test]
|
||||
fn test_listening_to_same_event_and_multi_type() {
|
||||
let (event_sender, manager_queue) = channel();
|
||||
let event_man_receiver = EventReceiver {
|
||||
mpsc_receiver: manager_queue,
|
||||
};
|
||||
let mut event_man: EventManager<SendError<Event>, Event> =
|
||||
EventManager::new(Box::new(event_man_receiver));
|
||||
let event_0 = Event::new(Severity::INFO, 0, 5).unwrap();
|
||||
let event_1 = Event::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let (event_sender, mut event_man) = generic_event_man();
|
||||
let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap();
|
||||
let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let (event_0_tx_0, event_0_rx_0) = channel();
|
||||
let (event_0_tx_1, event_0_rx_1) = channel();
|
||||
let event_listener_0 = MpscEventSenderQueue {
|
||||
@ -300,22 +641,26 @@ mod tests {
|
||||
id: 1,
|
||||
mpsc_sender: event_0_tx_1,
|
||||
};
|
||||
event_man.subscribe_single(event_0, event_listener_0.clone());
|
||||
event_man.subscribe_single(event_0, event_listener_1);
|
||||
let event_listener_0_sender_id = event_listener_0.id();
|
||||
event_man.subscribe_single(&event_0, event_listener_0_sender_id);
|
||||
event_man.add_sender(event_listener_0);
|
||||
let event_listener_1_sender_id = event_listener_1.id();
|
||||
event_man.subscribe_single(&event_0, event_listener_1_sender_id);
|
||||
event_man.add_sender(event_listener_1);
|
||||
event_sender
|
||||
.send(event_0)
|
||||
.send((event_0, None))
|
||||
.expect("Triggering Event 0 failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
check_handled_event(res.unwrap(), event_0, 2);
|
||||
check_next_event(event_0, &event_0_rx_0);
|
||||
check_next_event(event_0, &event_0_rx_1);
|
||||
event_man.subscribe_group(event_1.group_id(), event_listener_0.clone());
|
||||
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
|
||||
event_sender
|
||||
.send(event_0)
|
||||
.send((event_0, None))
|
||||
.expect("Triggering Event 0 failed");
|
||||
event_sender
|
||||
.send(event_1)
|
||||
.send((event_1, None))
|
||||
.expect("Triggering Event 1 failed");
|
||||
|
||||
// 3 Events messages will be sent now
|
||||
@ -329,13 +674,45 @@ mod tests {
|
||||
check_next_event(event_0, &event_0_rx_0);
|
||||
check_next_event(event_1, &event_0_rx_0);
|
||||
|
||||
// Double insertion should be detected, result should remain the same
|
||||
event_man.subscribe_group(event_1.group_id(), event_listener_0);
|
||||
// Do double insertion and then remove duplicates
|
||||
event_man.subscribe_group(event_1.group_id(), event_listener_0_sender_id);
|
||||
event_man.remove_duplicates(&ListenerKey::Group(event_1.group_id()));
|
||||
event_sender
|
||||
.send(event_1)
|
||||
.send((event_1, None))
|
||||
.expect("Triggering Event 1 failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
check_handled_event(res.unwrap(), event_1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_events_listener() {
|
||||
let (event_sender, manager_queue) = channel();
|
||||
let event_man_receiver = MpscEventReceiver::new(manager_queue);
|
||||
let mut event_man: EventManager<SendError<EventU32WithAuxData>> =
|
||||
EventManager::new(Box::new(event_man_receiver));
|
||||
let event_0 = EventU32::new(Severity::INFO, 0, 5).unwrap();
|
||||
let event_1 = EventU32::new(Severity::HIGH, 1, 0).unwrap();
|
||||
let (event_0_tx_0, all_events_rx) = channel();
|
||||
let all_events_listener = MpscEventSenderQueue {
|
||||
id: 0,
|
||||
mpsc_sender: event_0_tx_0,
|
||||
};
|
||||
event_man.subscribe_all(all_events_listener.id());
|
||||
event_man.add_sender(all_events_listener);
|
||||
event_sender
|
||||
.send((event_0, None))
|
||||
.expect("Triggering event 0 failed");
|
||||
event_sender
|
||||
.send((event_1, None))
|
||||
.expect("Triggering event 1 failed");
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
check_handled_event(res.unwrap(), event_0, 1);
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
check_handled_event(res.unwrap(), event_1, 1);
|
||||
check_next_event(event_0, &all_events_rx);
|
||||
check_next_event(event_1, &all_events_rx);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,35 @@
|
||||
//! Event support module
|
||||
|
||||
//!
|
||||
//! This module includes the basic event structs [EventU32] and [EventU16] and versions with the
|
||||
//! ECSS severity levels as a type parameter. These structs are simple abstractions on top of the
|
||||
//! [u32] and [u16] types where the raw value is the unique identifier for a particular event.
|
||||
//! The abstraction also allows to group related events using a group ID, and the severity
|
||||
//! of an event is encoded inside the raw value itself with four possible [Severity] levels:
|
||||
//!
|
||||
//! - INFO
|
||||
//! - LOW
|
||||
//! - MEDIUM
|
||||
//! - HIGH
|
||||
//!
|
||||
//! All event structs implement the [EcssEnumeration] trait and can be created as constants.
|
||||
//! This allows to easily create a static list of constant events which can then be used to generate
|
||||
//! event telemetry using the PUS event manager modules.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use fsrc_core::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
|
||||
//!
|
||||
//! const MSG_RECVD: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::const_new(1, 0);
|
||||
//! const MSG_FAILED: EventU32 = EventU32::const_new(Severity::LOW, 1, 1);
|
||||
//!
|
||||
//! const TEMPERATURE_HIGH: EventU32TypedSev<SeverityHigh> = EventU32TypedSev::const_new(2, 0);
|
||||
//!
|
||||
//! let small_event = EventU16::new(Severity::INFO, 3, 0);
|
||||
//! ```
|
||||
use core::fmt::Debug;
|
||||
use core::hash::Hash;
|
||||
use delegate::delegate;
|
||||
use spacepackets::ecss::{EcssEnumeration, ToBeBytes};
|
||||
use spacepackets::{ByteConversionError, SizeMissmatch};
|
||||
use std::marker::PhantomData;
|
||||
@ -18,7 +47,39 @@ pub enum Severity {
|
||||
HIGH = 3,
|
||||
}
|
||||
|
||||
pub trait EventProvider: PartialEq + Eq + Copy + Clone + Hash {
|
||||
pub trait HasSeverity: Debug + PartialEq + Eq + Copy + Clone {
|
||||
const SEVERITY: Severity;
|
||||
}
|
||||
|
||||
/// Type level support struct
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct SeverityInfo {}
|
||||
impl HasSeverity for SeverityInfo {
|
||||
const SEVERITY: Severity = Severity::INFO;
|
||||
}
|
||||
|
||||
/// Type level support struct
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct SeverityLow {}
|
||||
impl HasSeverity for SeverityLow {
|
||||
const SEVERITY: Severity = Severity::LOW;
|
||||
}
|
||||
|
||||
/// Type level support struct
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct SeverityMedium {}
|
||||
impl HasSeverity for SeverityMedium {
|
||||
const SEVERITY: Severity = Severity::MEDIUM;
|
||||
}
|
||||
|
||||
/// Type level support struct
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
pub struct SeverityHigh {}
|
||||
impl HasSeverity for SeverityHigh {
|
||||
const SEVERITY: Severity = Severity::HIGH;
|
||||
}
|
||||
|
||||
pub trait GenericEvent: EcssEnumeration {
|
||||
type Raw;
|
||||
type GroupId;
|
||||
type UniqueId;
|
||||
@ -47,7 +108,7 @@ impl TryFrom<u8> for Severity {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventBase<RAW, GID, UID> {
|
||||
struct EventBase<RAW, GID, UID> {
|
||||
severity: Severity,
|
||||
group_id: GID,
|
||||
unique_id: UID,
|
||||
@ -149,28 +210,96 @@ macro_rules! event_provider_impl {
|
||||
}
|
||||
};
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Event {
|
||||
base: EventBase<u32, u16, u16>,
|
||||
}
|
||||
|
||||
impl EventProvider for Event {
|
||||
type Raw = u32;
|
||||
type GroupId = u16;
|
||||
type UniqueId = u16;
|
||||
macro_rules! impl_event_provider {
|
||||
($BaseIdent: ident, $TypedIdent: ident, $raw: ty, $gid: ty, $uid: ty) => {
|
||||
impl GenericEvent for $BaseIdent {
|
||||
type Raw = $raw;
|
||||
type GroupId = $gid;
|
||||
type UniqueId = $uid;
|
||||
|
||||
event_provider_impl!();
|
||||
|
||||
fn raw_as_largest_type(&self) -> LargestEventRaw {
|
||||
self.raw()
|
||||
self.raw().into()
|
||||
}
|
||||
|
||||
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw {
|
||||
self.group_id()
|
||||
self.group_id().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<SEVERITY: HasSeverity> GenericEvent for $TypedIdent<SEVERITY> {
|
||||
type Raw = $raw;
|
||||
type GroupId = $gid;
|
||||
type UniqueId = $uid;
|
||||
|
||||
delegate!(to self.event {
|
||||
fn raw(&self) -> Self::Raw;
|
||||
fn severity(&self) -> Severity;
|
||||
fn group_id(&self) -> Self::GroupId;
|
||||
fn unique_id(&self) -> Self::UniqueId;
|
||||
fn raw_as_largest_type(&self) -> LargestEventRaw;
|
||||
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
macro_rules! try_from_impls {
|
||||
($SevIdent: ident, $severity: path, $raw: ty, $TypedSevIdent: ident) => {
|
||||
impl TryFrom<$raw> for $TypedSevIdent<$SevIdent> {
|
||||
type Error = Severity;
|
||||
|
||||
fn try_from(raw: $raw) -> Result<Self, Self::Error> {
|
||||
Self::try_from_generic($severity, raw)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! const_from_fn {
|
||||
($from_fn_name: ident, $TypedIdent: ident, $SevIdent: ident) => {
|
||||
pub const fn $from_fn_name(event: $TypedIdent<$SevIdent>) -> Self {
|
||||
Self {
|
||||
base: event.event.base,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventU32 {
|
||||
base: EventBase<u32, u16, u16>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventU32TypedSev<SEVERITY> {
|
||||
event: EventU32,
|
||||
phantom: PhantomData<SEVERITY>,
|
||||
}
|
||||
|
||||
impl<SEVERITY: HasSeverity> From<EventU32TypedSev<SEVERITY>> for EventU32 {
|
||||
fn from(e: EventU32TypedSev<SEVERITY>) -> Self {
|
||||
Self { base: e.event.base }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> AsRef<EventU32> for EventU32TypedSev<Severity> {
|
||||
fn as_ref(&self) -> &EventU32 {
|
||||
&self.event
|
||||
}
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> AsMut<EventU32> for EventU32TypedSev<Severity> {
|
||||
fn as_mut(&mut self) -> &mut EventU32 {
|
||||
&mut self.event
|
||||
}
|
||||
}
|
||||
|
||||
impl_event_provider!(EventU32, EventU32TypedSev, u32, u16, u16);
|
||||
|
||||
impl EventU32 {
|
||||
/// Generate an event. The raw representation of an event has 32 bits.
|
||||
/// If the passed group ID is invalid (too large), None wil be returned
|
||||
///
|
||||
@ -184,8 +313,8 @@ impl Event {
|
||||
/// raw event ID
|
||||
pub fn new(
|
||||
severity: Severity,
|
||||
group_id: <Self as EventProvider>::GroupId,
|
||||
unique_id: <Self as EventProvider>::UniqueId,
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Option<Self> {
|
||||
if group_id > (2u16.pow(14) - 1) {
|
||||
return None;
|
||||
@ -199,13 +328,10 @@ impl Event {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Const version of [new], but panics on invalid input which is invalid group ID
|
||||
/// values
|
||||
pub const fn const_new(
|
||||
severity: Severity,
|
||||
group_id: <Self as EventProvider>::GroupId,
|
||||
unique_id: <Self as EventProvider>::UniqueId,
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Self {
|
||||
if group_id > (2u16.pow(14) - 1) {
|
||||
panic!("Group ID too large");
|
||||
@ -219,9 +345,52 @@ impl Event {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const_from_fn!(const_from_info, EventU32TypedSev, SeverityInfo);
|
||||
const_from_fn!(const_from_low, EventU32TypedSev, SeverityLow);
|
||||
const_from_fn!(const_from_medium, EventU32TypedSev, SeverityMedium);
|
||||
const_from_fn!(const_from_high, EventU32TypedSev, SeverityHigh);
|
||||
}
|
||||
|
||||
impl From<u32> for Event {
|
||||
impl<SEVERITY: HasSeverity> EventU32TypedSev<SEVERITY> {
|
||||
/// This is similar to [EventU32::new] but the severity is a type generic, which allows to
|
||||
/// have distinct types for events with different severities
|
||||
pub fn new(
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Option<Self> {
|
||||
let event = EventU32::new(SEVERITY::SEVERITY, group_id, unique_id)?;
|
||||
Some(Self {
|
||||
event,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Const version of [Self::new], but panics on invalid group ID input values.
|
||||
pub const fn const_new(
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Self {
|
||||
let event = EventU32::const_new(SEVERITY::SEVERITY, group_id, unique_id);
|
||||
Self {
|
||||
event,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from_generic(expected: Severity, raw: u32) -> Result<Self, Severity> {
|
||||
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
||||
if severity != expected {
|
||||
return Err(severity);
|
||||
}
|
||||
Ok(Self::const_new(
|
||||
((raw >> 16) & 0x3FFF) as u16,
|
||||
(raw & 0xFFFF) as u16,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for EventU32 {
|
||||
fn from(raw: u32) -> Self {
|
||||
// Severity conversion from u8 should never fail
|
||||
let severity = Severity::try_from(((raw >> 30) & 0b11) as u8).unwrap();
|
||||
@ -232,22 +401,53 @@ impl From<u32> for Event {
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssEnumeration for Event {
|
||||
try_from_impls!(SeverityInfo, Severity::INFO, u32, EventU32TypedSev);
|
||||
try_from_impls!(SeverityLow, Severity::LOW, u32, EventU32TypedSev);
|
||||
try_from_impls!(SeverityMedium, Severity::MEDIUM, u32, EventU32TypedSev);
|
||||
try_from_impls!(SeverityHigh, Severity::HIGH, u32, EventU32TypedSev);
|
||||
|
||||
impl EcssEnumeration for EventU32 {
|
||||
fn pfc(&self) -> u8 {
|
||||
32
|
||||
}
|
||||
|
||||
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||
self.base.write_to_bytes(self.raw(), buf, self.byte_width())
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection RsTraitImplementation
|
||||
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU32TypedSev<SEVERITY> {
|
||||
delegate!(to self.event {
|
||||
fn pfc(&self) -> u8;
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventSmall {
|
||||
pub struct EventU16 {
|
||||
base: EventBase<u16, u8, u8>,
|
||||
}
|
||||
|
||||
impl EventSmall {
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventU16TypedSev<SEVERITY> {
|
||||
event: EventU16,
|
||||
phantom: PhantomData<SEVERITY>,
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> AsRef<EventU16> for EventU16TypedSev<Severity> {
|
||||
fn as_ref(&self) -> &EventU16 {
|
||||
&self.event
|
||||
}
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> AsMut<EventU16> for EventU16TypedSev<Severity> {
|
||||
fn as_mut(&mut self) -> &mut EventU16 {
|
||||
&mut self.event
|
||||
}
|
||||
}
|
||||
|
||||
impl EventU16 {
|
||||
/// Generate a small event. The raw representation of a small event has 16 bits.
|
||||
/// If the passed group ID is invalid (too large), [None] wil be returned
|
||||
///
|
||||
@ -261,8 +461,8 @@ impl EventSmall {
|
||||
/// raw event ID
|
||||
pub fn new(
|
||||
severity: Severity,
|
||||
group_id: <Self as EventProvider>::GroupId,
|
||||
unique_id: <Self as EventProvider>::UniqueId,
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Option<Self> {
|
||||
if group_id > (2u8.pow(6) - 1) {
|
||||
return None;
|
||||
@ -277,10 +477,11 @@ impl EventSmall {
|
||||
})
|
||||
}
|
||||
|
||||
/// Const version of [Self::new], but panics on invalid group ID input values.
|
||||
pub const fn const_new(
|
||||
severity: Severity,
|
||||
group_id: <Self as EventProvider>::GroupId,
|
||||
unique_id: <Self as EventProvider>::UniqueId,
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Self {
|
||||
if group_id > (2u8.pow(6) - 1) {
|
||||
panic!("Group ID too large");
|
||||
@ -294,36 +495,73 @@ impl EventSmall {
|
||||
},
|
||||
}
|
||||
}
|
||||
const_from_fn!(const_from_info, EventU16TypedSev, SeverityInfo);
|
||||
const_from_fn!(const_from_low, EventU16TypedSev, SeverityLow);
|
||||
const_from_fn!(const_from_medium, EventU16TypedSev, SeverityMedium);
|
||||
const_from_fn!(const_from_high, EventU16TypedSev, SeverityHigh);
|
||||
}
|
||||
|
||||
impl EventProvider for EventSmall {
|
||||
type Raw = u16;
|
||||
type GroupId = u8;
|
||||
type UniqueId = u8;
|
||||
|
||||
event_provider_impl!();
|
||||
|
||||
fn raw_as_largest_type(&self) -> LargestEventRaw {
|
||||
self.raw().into()
|
||||
impl<SEVERITY: HasSeverity> EventU16TypedSev<SEVERITY> {
|
||||
/// This is similar to [EventU16::new] but the severity is a type generic, which allows to
|
||||
/// have distinct types for events with different severities
|
||||
pub fn new(
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Option<Self> {
|
||||
let event = EventU16::new(SEVERITY::SEVERITY, group_id, unique_id)?;
|
||||
Some(Self {
|
||||
event,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn group_id_as_largest_type(&self) -> LargestGroupIdRaw {
|
||||
self.group_id().into()
|
||||
/// Const version of [Self::new], but panics on invalid group ID input values.
|
||||
pub const fn const_new(
|
||||
group_id: <Self as GenericEvent>::GroupId,
|
||||
unique_id: <Self as GenericEvent>::UniqueId,
|
||||
) -> Self {
|
||||
let event = EventU16::const_new(SEVERITY::SEVERITY, group_id, unique_id);
|
||||
Self {
|
||||
event,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from_generic(expected: Severity, raw: u16) -> Result<Self, Severity> {
|
||||
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
|
||||
if severity != expected {
|
||||
return Err(severity);
|
||||
}
|
||||
Ok(Self::const_new(
|
||||
((raw >> 8) & 0x3F) as u8,
|
||||
(raw & 0xFF) as u8,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssEnumeration for EventSmall {
|
||||
impl_event_provider!(EventU16, EventU16TypedSev, u16, u8, u8);
|
||||
|
||||
impl EcssEnumeration for EventU16 {
|
||||
#[inline]
|
||||
fn pfc(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
|
||||
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||
self.base.write_to_bytes(self.raw(), buf, self.byte_width())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for EventSmall {
|
||||
fn from(raw: <Self as EventProvider>::Raw) -> Self {
|
||||
//noinspection RsTraitImplementation
|
||||
impl<SEVERITY: HasSeverity> EcssEnumeration for EventU16TypedSev<SEVERITY> {
|
||||
delegate!(to self.event {
|
||||
fn pfc(&self) -> u8;
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
|
||||
});
|
||||
}
|
||||
|
||||
impl From<u16> for EventU16 {
|
||||
fn from(raw: <Self as GenericEvent>::Raw) -> Self {
|
||||
let severity = Severity::try_from(((raw >> 14) & 0b11) as u8).unwrap();
|
||||
let group_id = ((raw >> 8) & 0x3F) as u8;
|
||||
let unique_id = (raw & 0xFF) as u8;
|
||||
@ -332,10 +570,43 @@ impl From<u16> for EventSmall {
|
||||
}
|
||||
}
|
||||
|
||||
try_from_impls!(SeverityInfo, Severity::INFO, u16, EventU16TypedSev);
|
||||
try_from_impls!(SeverityLow, Severity::LOW, u16, EventU16TypedSev);
|
||||
try_from_impls!(SeverityMedium, Severity::MEDIUM, u16, EventU16TypedSev);
|
||||
try_from_impls!(SeverityHigh, Severity::HIGH, u16, EventU16TypedSev);
|
||||
|
||||
impl<Severity: HasSeverity> PartialEq<EventU32> for EventU32TypedSev<Severity> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &EventU32) -> bool {
|
||||
self.raw() == other.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> PartialEq<EventU32TypedSev<Severity>> for EventU32 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &EventU32TypedSev<Severity>) -> bool {
|
||||
self.raw() == other.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> PartialEq<EventU16> for EventU16TypedSev<Severity> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &EventU16) -> bool {
|
||||
self.raw() == other.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Severity: HasSeverity> PartialEq<EventU16TypedSev<Severity>> for EventU16 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &EventU16TypedSev<Severity>) -> bool {
|
||||
self.raw() == other.raw()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Event;
|
||||
use crate::events::{EventProvider, EventSmall, Severity};
|
||||
use super::EventU32TypedSev;
|
||||
use super::*;
|
||||
use spacepackets::ecss::EcssEnumeration;
|
||||
use spacepackets::ByteConversionError;
|
||||
use std::mem::size_of;
|
||||
@ -344,20 +615,27 @@ mod tests {
|
||||
assert_eq!(size_of::<T>(), val);
|
||||
}
|
||||
|
||||
const INFO_EVENT: Event = Event::const_new(Severity::INFO, 0, 0);
|
||||
const INFO_EVENT_SMALL: EventSmall = EventSmall::const_new(Severity::INFO, 0, 0);
|
||||
const HIGH_SEV_EVENT: Event = Event::const_new(Severity::HIGH, 0x3FFF, 0xFFFF);
|
||||
const HIGH_SEV_EVENT_SMALL: EventSmall = EventSmall::const_new(Severity::HIGH, 0x3F, 0xff);
|
||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::const_new(0, 0);
|
||||
const INFO_EVENT_SMALL: EventU16TypedSev<SeverityInfo> = EventU16TypedSev::const_new(0, 0);
|
||||
const HIGH_SEV_EVENT: EventU32TypedSev<SeverityHigh> =
|
||||
EventU32TypedSev::const_new(0x3FFF, 0xFFFF);
|
||||
const HIGH_SEV_EVENT_SMALL: EventU16TypedSev<SeverityHigh> =
|
||||
EventU16TypedSev::const_new(0x3F, 0xff);
|
||||
|
||||
/// This working is a test in itself.
|
||||
const INFO_REDUCED: EventU32 = EventU32::const_from_info(INFO_EVENT);
|
||||
|
||||
#[test]
|
||||
fn test_normal_from_raw_conversion() {
|
||||
let conv_from_raw = Event::from(INFO_EVENT.raw());
|
||||
let conv_from_raw = EventU32TypedSev::<SeverityInfo>::try_from(INFO_EVENT.raw())
|
||||
.expect("Creating typed EventU32 failed");
|
||||
assert_eq!(conv_from_raw, INFO_EVENT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_small_from_raw_conversion() {
|
||||
let conv_from_raw = EventSmall::from(INFO_EVENT_SMALL.raw());
|
||||
let conv_from_raw = EventU16TypedSev::<SeverityInfo>::try_from(INFO_EVENT_SMALL.raw())
|
||||
.expect("Creating typed EventU16 failed");
|
||||
assert_eq!(conv_from_raw, INFO_EVENT_SMALL);
|
||||
}
|
||||
|
||||
@ -409,18 +687,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn invalid_group_id_normal() {
|
||||
assert!(Event::new(Severity::MEDIUM, 2_u16.pow(14), 0).is_none());
|
||||
assert!(EventU32TypedSev::<SeverityMedium>::new(2_u16.pow(14), 0).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_group_id_small() {
|
||||
assert!(EventSmall::new(Severity::MEDIUM, 2_u8.pow(6), 0).is_none());
|
||||
assert!(EventU16TypedSev::<SeverityMedium>::new(2_u8.pow(6), 0).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regular_new() {
|
||||
assert_eq!(
|
||||
Event::new(Severity::INFO, 0, 0).expect("Creating regular event failed"),
|
||||
EventU32TypedSev::<SeverityInfo>::new(0, 0).expect("Creating regular event failed"),
|
||||
INFO_EVENT
|
||||
);
|
||||
}
|
||||
@ -428,7 +706,7 @@ mod tests {
|
||||
#[test]
|
||||
fn small_new() {
|
||||
assert_eq!(
|
||||
EventSmall::new(Severity::INFO, 0, 0).expect("Creating regular event failed"),
|
||||
EventU16TypedSev::<SeverityInfo>::new(0, 0).expect("Creating regular event failed"),
|
||||
INFO_EVENT_SMALL
|
||||
);
|
||||
}
|
||||
@ -464,7 +742,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_to_buf() {
|
||||
let mut buf: [u8; 4] = [0; 4];
|
||||
assert!(HIGH_SEV_EVENT.write_to_bytes(&mut buf).is_ok());
|
||||
assert!(HIGH_SEV_EVENT.write_to_be_bytes(&mut buf).is_ok());
|
||||
let val_from_raw = u32::from_be_bytes(buf);
|
||||
assert_eq!(val_from_raw, 0xFFFFFFFF);
|
||||
}
|
||||
@ -472,7 +750,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_to_buf_small() {
|
||||
let mut buf: [u8; 2] = [0; 2];
|
||||
assert!(HIGH_SEV_EVENT_SMALL.write_to_bytes(&mut buf).is_ok());
|
||||
assert!(HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf).is_ok());
|
||||
let val_from_raw = u16::from_be_bytes(buf);
|
||||
assert_eq!(val_from_raw, 0xFFFF);
|
||||
}
|
||||
@ -480,7 +758,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_to_buf_insufficient_buf() {
|
||||
let mut buf: [u8; 3] = [0; 3];
|
||||
let err = HIGH_SEV_EVENT.write_to_bytes(&mut buf);
|
||||
let err = HIGH_SEV_EVENT.write_to_be_bytes(&mut buf);
|
||||
assert!(err.is_err());
|
||||
let err = err.unwrap_err();
|
||||
if let ByteConversionError::ToSliceTooSmall(missmatch) = err {
|
||||
@ -492,7 +770,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_to_buf_small_insufficient_buf() {
|
||||
let mut buf: [u8; 1] = [0; 1];
|
||||
let err = HIGH_SEV_EVENT_SMALL.write_to_bytes(&mut buf);
|
||||
let err = HIGH_SEV_EVENT_SMALL.write_to_be_bytes(&mut buf);
|
||||
assert!(err.is_err());
|
||||
let err = err.unwrap_err();
|
||||
if let ByteConversionError::ToSliceTooSmall(missmatch) = err {
|
||||
@ -508,4 +786,19 @@ mod tests {
|
||||
let invalid = Severity::HIGH as u8 + 1;
|
||||
assert!(Severity::try_from(invalid).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reduction() {
|
||||
let event = EventU32TypedSev::<SeverityInfo>::const_new(1, 1);
|
||||
let raw = event.raw();
|
||||
let reduced: EventU32 = event.into();
|
||||
assert_eq!(reduced.group_id(), 1);
|
||||
assert_eq!(reduced.unique_id(), 1);
|
||||
assert_eq!(raw, reduced.raw());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_reducation() {
|
||||
assert_eq!(INFO_REDUCED.raw(), INFO_EVENT.raw());
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! # Hardware Abstraction Layer module
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod host;
|
||||
|
@ -10,20 +10,23 @@
|
||||
#![no_std]
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
extern crate downcast_rs;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
extern crate std;
|
||||
|
||||
pub mod error;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
pub mod event_man;
|
||||
pub mod events;
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||
pub mod executable;
|
||||
pub mod hal;
|
||||
pub mod objects;
|
||||
pub mod params;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
pub mod pool;
|
||||
pub mod pus;
|
||||
pub mod tmtc;
|
||||
|
||||
extern crate downcast_rs;
|
||||
|
643
fsrc-core/src/params.rs
Normal file
643
fsrc-core/src/params.rs
Normal file
@ -0,0 +1,643 @@
|
||||
//! Parameter types and enums.
|
||||
//!
|
||||
//! This module contains various helper types.
|
||||
//!
|
||||
//! # Primtive Parameter Wrappers and Enumeration
|
||||
//!
|
||||
//! This module includes wrapper for primitive rust types using the newtype pattern.
|
||||
//! This was also done for pairs and triplets of these primitive types.
|
||||
//! The [WritableToBeBytes] was implemented for all those types as well, which allows to easily
|
||||
//! convert them into a network friendly raw byte format. The [ParamsRaw] enumeration groups
|
||||
//! all newtypes and implements the [WritableToBeBytes] trait itself.
|
||||
//!
|
||||
//! ## Example for primitive type wrapper
|
||||
//!
|
||||
//! ```
|
||||
//! use fsrc_core::params::{ParamsRaw, ToBeBytes, U32Pair, WritableToBeBytes};
|
||||
//!
|
||||
//! let u32_pair = U32Pair(0x1010, 25);
|
||||
//! assert_eq!(u32_pair.0, 0x1010);
|
||||
//! assert_eq!(u32_pair.1, 25);
|
||||
//! // Convert to raw stream
|
||||
//! let raw_buf = u32_pair.to_be_bytes();
|
||||
//! assert_eq!(raw_buf, [0, 0, 0x10, 0x10, 0, 0, 0, 25]);
|
||||
//!
|
||||
//! // Convert to enum variant
|
||||
//! let params_raw: ParamsRaw = u32_pair.into();
|
||||
//! assert_eq!(params_raw, (0x1010_u32, 25_u32).into());
|
||||
//!
|
||||
//! // Convert to stream using the enum variant
|
||||
//! let mut other_raw_buf: [u8; 8] = [0; 8];
|
||||
//! params_raw.write_to_be_bytes(&mut other_raw_buf).expect("Writing parameter to buffer failed");
|
||||
//! assert_eq!(other_raw_buf, [0, 0, 0x10, 0x10, 0, 0, 0, 25]);
|
||||
//!
|
||||
//! // Create a pair from a raw stream
|
||||
//! let u32_pair_from_stream: U32Pair = raw_buf.as_slice().try_into().unwrap();
|
||||
//! assert_eq!(u32_pair_from_stream.0, 0x1010);
|
||||
//! assert_eq!(u32_pair_from_stream.1, 25);
|
||||
//! ```
|
||||
//!
|
||||
//! # Generic Parameter Enumeration
|
||||
//!
|
||||
//! The module also contains generic parameter enumerations.
|
||||
//! This includes the [ParamsHeapless] enumeration for contained values which do not require heap
|
||||
//! allocation, and the [Params] which enumerates [ParamsHeapless] and some additional types which
|
||||
//! require [alloc] support but allow for more flexbility.
|
||||
use crate::pool::StoreAddr;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::ToString;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
use core::mem::size_of;
|
||||
use paste::paste;
|
||||
pub use spacepackets::ecss::ToBeBytes;
|
||||
use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU64, EcssEnumU8, EcssEnumeration};
|
||||
use spacepackets::ByteConversionError;
|
||||
use spacepackets::SizeMissmatch;
|
||||
|
||||
/// Generic trait which is used for objects which can be converted into a raw network (big) endian
|
||||
/// byte format.
|
||||
pub trait WritableToBeBytes {
|
||||
fn raw_len(&self) -> usize;
|
||||
/// Writes the object to a raw buffer in network endianness (big)
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
|
||||
}
|
||||
|
||||
macro_rules! param_to_be_bytes_impl {
|
||||
($Newtype: ident) => {
|
||||
impl WritableToBeBytes for $Newtype {
|
||||
#[inline]
|
||||
fn raw_len(&self) -> usize {
|
||||
size_of::<<Self as ToBeBytes>::ByteArray>()
|
||||
}
|
||||
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
let raw_len = self.raw_len();
|
||||
if buf.len() < raw_len {
|
||||
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||
found: buf.len(),
|
||||
expected: raw_len,
|
||||
}));
|
||||
}
|
||||
buf[0..raw_len].copy_from_slice(&self.to_be_bytes());
|
||||
Ok(raw_len)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! primitive_newtypes_with_eq {
|
||||
($($ty: ty,)+) => {
|
||||
$(
|
||||
paste! {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct [<$ty:upper>](pub $ty);
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct [<$ty:upper Pair>](pub $ty, pub $ty);
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct [<$ty:upper Triplet>](pub $ty, pub $ty, pub $ty);
|
||||
|
||||
param_to_be_bytes_impl!([<$ty:upper>]);
|
||||
param_to_be_bytes_impl!([<$ty:upper Pair>]);
|
||||
param_to_be_bytes_impl!([<$ty:upper Triplet>]);
|
||||
|
||||
impl From<$ty> for [<$ty:upper>] {
|
||||
fn from(v: $ty) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
impl From<($ty, $ty)> for [<$ty:upper Pair>] {
|
||||
fn from(v: ($ty, $ty)) -> Self {
|
||||
Self(v.0, v.1)
|
||||
}
|
||||
}
|
||||
impl From<($ty, $ty, $ty)> for [<$ty:upper Triplet>] {
|
||||
fn from(v: ($ty, $ty, $ty)) -> Self {
|
||||
Self(v.0, v.1, v.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! primitive_newtypes {
|
||||
($($ty: ty,)+) => {
|
||||
$(
|
||||
paste! {
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct [<$ty:upper>](pub $ty);
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct [<$ty:upper Pair>](pub $ty, pub $ty);
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct [<$ty:upper Triplet>](pub $ty, pub $ty, pub $ty);
|
||||
|
||||
param_to_be_bytes_impl!([<$ty:upper>]);
|
||||
param_to_be_bytes_impl!([<$ty:upper Pair>]);
|
||||
param_to_be_bytes_impl!([<$ty:upper Triplet>]);
|
||||
|
||||
impl From<$ty> for [<$ty:upper>] {
|
||||
fn from(v: $ty) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
impl From<($ty, $ty)> for [<$ty:upper Pair>] {
|
||||
fn from(v: ($ty, $ty)) -> Self {
|
||||
Self(v.0, v.1)
|
||||
}
|
||||
}
|
||||
impl From<($ty, $ty, $ty)> for [<$ty:upper Triplet>] {
|
||||
fn from(v: ($ty, $ty, $ty)) -> Self {
|
||||
Self(v.0, v.1, v.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
primitive_newtypes_with_eq!(u8, u16, u32, u64, i8, i16, i32, i64,);
|
||||
primitive_newtypes!(f32, f64,);
|
||||
|
||||
macro_rules! scalar_byte_conversions_impl {
|
||||
($($ty: ty,)+) => {
|
||||
$(
|
||||
paste! {
|
||||
impl ToBeBytes for [<$ty:upper>] {
|
||||
type ByteArray = [u8; size_of::<$ty>()];
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
self.0.to_be_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for [<$ty:upper>] {
|
||||
type Error = ByteConversionError;
|
||||
|
||||
fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
|
||||
if v.len() < size_of::<$ty>() {
|
||||
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||
expected: size_of::<$ty>(),
|
||||
found: v.len()
|
||||
}));
|
||||
}
|
||||
Ok([<$ty:upper>]($ty::from_be_bytes(v[0..size_of::<$ty>()].try_into().unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! pair_byte_conversions_impl {
|
||||
($($ty: ty,)+) => {
|
||||
$(
|
||||
paste! {
|
||||
impl ToBeBytes for [<$ty:upper Pair>] {
|
||||
type ByteArray = [u8; size_of::<$ty>() * 2];
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
let mut array = [0; size_of::<$ty>() * 2];
|
||||
array[0..size_of::<$ty>()].copy_from_slice(&self.0.to_be_bytes());
|
||||
array[
|
||||
size_of::<$ty>()..2 * size_of::<$ty>()
|
||||
].copy_from_slice(&self.1.to_be_bytes());
|
||||
array
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for [<$ty:upper Pair>] {
|
||||
type Error = ByteConversionError;
|
||||
|
||||
fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
|
||||
if v.len() < 2 * size_of::<$ty>() {
|
||||
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||
expected: 2 * size_of::<$ty>(),
|
||||
found: v.len()
|
||||
}));
|
||||
}
|
||||
Ok([<$ty:upper Pair>](
|
||||
$ty::from_be_bytes(v[0..size_of::<$ty>()].try_into().unwrap()),
|
||||
$ty::from_be_bytes(v[size_of::<$ty>()..2 * size_of::<$ty>()].try_into().unwrap())
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! triplet_to_be_bytes_impl {
|
||||
($($ty: ty,)+) => {
|
||||
$(
|
||||
paste! {
|
||||
impl ToBeBytes for [<$ty:upper Triplet>] {
|
||||
type ByteArray = [u8; size_of::<$ty>() * 3];
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
let mut array = [0; size_of::<$ty>() * 3];
|
||||
array[0..size_of::<$ty>()].copy_from_slice(&self.0.to_be_bytes());
|
||||
array[
|
||||
size_of::<$ty>()..2 * size_of::<$ty>()
|
||||
].copy_from_slice(&self.1.to_be_bytes());
|
||||
array[
|
||||
2 * size_of::<$ty>()..3 * size_of::<$ty>()
|
||||
].copy_from_slice(&self.2.to_be_bytes());
|
||||
array
|
||||
}
|
||||
}
|
||||
impl TryFrom<&[u8]> for [<$ty:upper Triplet>] {
|
||||
type Error = ByteConversionError;
|
||||
|
||||
fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
|
||||
if v.len() < 3 * size_of::<$ty>() {
|
||||
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||
expected: 3 * size_of::<$ty>(),
|
||||
found: v.len()
|
||||
}));
|
||||
}
|
||||
Ok([<$ty:upper Triplet>](
|
||||
$ty::from_be_bytes(v[0..size_of::<$ty>()].try_into().unwrap()),
|
||||
$ty::from_be_bytes(v[size_of::<$ty>()..2 * size_of::<$ty>()].try_into().unwrap()),
|
||||
$ty::from_be_bytes(v[2 * size_of::<$ty>()..3 * size_of::<$ty>()].try_into().unwrap())
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
scalar_byte_conversions_impl!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64,);
|
||||
|
||||
impl ToBeBytes for U8Pair {
|
||||
type ByteArray = [u8; 2];
|
||||
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
let mut array = [0; 2];
|
||||
array[0] = self.0;
|
||||
array[1] = self.1;
|
||||
array
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBeBytes for I8Pair {
|
||||
type ByteArray = [u8; 2];
|
||||
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
let mut array = [0; 2];
|
||||
array[0] = self.0 as u8;
|
||||
array[1] = self.1 as u8;
|
||||
array
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBeBytes for U8Triplet {
|
||||
type ByteArray = [u8; 3];
|
||||
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
let mut array = [0; 3];
|
||||
array[0] = self.0;
|
||||
array[1] = self.1;
|
||||
array[2] = self.2;
|
||||
array
|
||||
}
|
||||
}
|
||||
|
||||
impl ToBeBytes for I8Triplet {
|
||||
type ByteArray = [u8; 3];
|
||||
|
||||
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||
let mut array = [0; 3];
|
||||
array[0] = self.0 as u8;
|
||||
array[1] = self.1 as u8;
|
||||
array[2] = self.2 as u8;
|
||||
array
|
||||
}
|
||||
}
|
||||
|
||||
pair_byte_conversions_impl!(u16, u32, u64, i16, i32, i64, f32, f64,);
|
||||
triplet_to_be_bytes_impl!(u16, u32, u64, i16, i32, i64, f32, f64,);
|
||||
|
||||
/// Generic enumeration for additonal parameters only consisting of primitive data types.
|
||||
///
|
||||
/// All contained variants and the enum itself implement the [WritableToBeBytes] trait, which
|
||||
/// allows to easily convert them into a network-friendly format.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum ParamsRaw {
|
||||
U8(U8),
|
||||
U8Pair(U8Pair),
|
||||
U8Triplet(U8Triplet),
|
||||
I8(I8),
|
||||
I8Pair(I8Pair),
|
||||
I8Triplet(I8Triplet),
|
||||
U16(U16),
|
||||
U16Pair(U16Pair),
|
||||
U16Triplet(U16Triplet),
|
||||
I16(I16),
|
||||
I16Pair(I16Pair),
|
||||
I16Triplet(I16Triplet),
|
||||
U32(U32),
|
||||
U32Pair(U32Pair),
|
||||
U32Triplet(U32Triplet),
|
||||
I32(I32),
|
||||
I32Pair(I32Pair),
|
||||
I32Triplet(I32Triplet),
|
||||
F32(F32),
|
||||
F32Pair(F32Pair),
|
||||
F32Triplet(F32Triplet),
|
||||
U64(U64),
|
||||
I64(I64),
|
||||
F64(F64),
|
||||
}
|
||||
|
||||
impl WritableToBeBytes for ParamsRaw {
|
||||
fn raw_len(&self) -> usize {
|
||||
match self {
|
||||
ParamsRaw::U8(v) => v.raw_len(),
|
||||
ParamsRaw::U8Pair(v) => v.raw_len(),
|
||||
ParamsRaw::U8Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::I8(v) => v.raw_len(),
|
||||
ParamsRaw::I8Pair(v) => v.raw_len(),
|
||||
ParamsRaw::I8Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::U16(v) => v.raw_len(),
|
||||
ParamsRaw::U16Pair(v) => v.raw_len(),
|
||||
ParamsRaw::U16Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::I16(v) => v.raw_len(),
|
||||
ParamsRaw::I16Pair(v) => v.raw_len(),
|
||||
ParamsRaw::I16Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::U32(v) => v.raw_len(),
|
||||
ParamsRaw::U32Pair(v) => v.raw_len(),
|
||||
ParamsRaw::U32Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::I32(v) => v.raw_len(),
|
||||
ParamsRaw::I32Pair(v) => v.raw_len(),
|
||||
ParamsRaw::I32Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::F32(v) => v.raw_len(),
|
||||
ParamsRaw::F32Pair(v) => v.raw_len(),
|
||||
ParamsRaw::F32Triplet(v) => v.raw_len(),
|
||||
ParamsRaw::U64(v) => v.raw_len(),
|
||||
ParamsRaw::I64(v) => v.raw_len(),
|
||||
ParamsRaw::F64(v) => v.raw_len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
match self {
|
||||
ParamsRaw::U8(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U8Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U8Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I8(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I8Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I8Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U16(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U16Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U16Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I16(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I16Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I16Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U32(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U32Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U32Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I32(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I32Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I32Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::F32(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::F32Pair(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::F32Triplet(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::U64(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::I64(v) => v.write_to_be_bytes(buf),
|
||||
ParamsRaw::F64(v) => v.write_to_be_bytes(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! params_raw_from_newtype {
|
||||
($($newtype: ident,)+) => {
|
||||
$(
|
||||
impl From<$newtype> for ParamsRaw {
|
||||
fn from(v: $newtype) -> Self {
|
||||
Self::$newtype(v)
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
params_raw_from_newtype!(
|
||||
U8, U8Pair, U8Triplet, U16, U16Pair, U16Triplet, U32, U32Pair, U32Triplet, I8, I8Pair,
|
||||
I8Triplet, I16, I16Pair, I16Triplet, I32, I32Pair, I32Triplet, F32, F32Pair, F32Triplet, U64,
|
||||
I64, F64,
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum EcssEnumParams {
|
||||
U8(EcssEnumU8),
|
||||
U16(EcssEnumU16),
|
||||
U32(EcssEnumU32),
|
||||
U64(EcssEnumU64),
|
||||
}
|
||||
|
||||
macro_rules! writable_as_be_bytes_ecss_enum_impl {
|
||||
($EnumIdent: ident) => {
|
||||
impl WritableToBeBytes for $EnumIdent {
|
||||
fn raw_len(&self) -> usize {
|
||||
self.byte_width()
|
||||
}
|
||||
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
EcssEnumeration::write_to_be_bytes(self, buf).map(|_| self.byte_width())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU8);
|
||||
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU16);
|
||||
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU32);
|
||||
writable_as_be_bytes_ecss_enum_impl!(EcssEnumU64);
|
||||
|
||||
impl WritableToBeBytes for EcssEnumParams {
|
||||
fn raw_len(&self) -> usize {
|
||||
match self {
|
||||
EcssEnumParams::U8(e) => e.byte_width(),
|
||||
EcssEnumParams::U16(e) => e.byte_width(),
|
||||
EcssEnumParams::U32(e) => e.byte_width(),
|
||||
EcssEnumParams::U64(e) => e.byte_width(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
||||
match self {
|
||||
EcssEnumParams::U8(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
|
||||
EcssEnumParams::U16(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
|
||||
EcssEnumParams::U32(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
|
||||
EcssEnumParams::U64(e) => WritableToBeBytes::write_to_be_bytes(e, buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic enumeration for parameters which do not rely on heap allocations.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum ParamsHeapless {
|
||||
Raw(ParamsRaw),
|
||||
EcssEnum(EcssEnumParams),
|
||||
Store(StoreAddr),
|
||||
}
|
||||
|
||||
impl From<StoreAddr> for ParamsHeapless {
|
||||
fn from(x: StoreAddr) -> Self {
|
||||
Self::Store(x)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_conversions_for_raw {
|
||||
($(($raw_ty: ty, $TargetPath: path),)+) => {
|
||||
$(
|
||||
impl From<$raw_ty> for ParamsRaw {
|
||||
fn from(val: $raw_ty) -> Self {
|
||||
$TargetPath(val.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$raw_ty> for ParamsHeapless {
|
||||
fn from(val: $raw_ty) -> Self {
|
||||
ParamsHeapless::Raw(val.into())
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
from_conversions_for_raw!(
|
||||
(u8, Self::U8),
|
||||
((u8, u8), Self::U8Pair),
|
||||
((u8, u8, u8), Self::U8Triplet),
|
||||
(i8, Self::I8),
|
||||
((i8, i8), Self::I8Pair),
|
||||
((i8, i8, i8), Self::I8Triplet),
|
||||
(u16, Self::U16),
|
||||
((u16, u16), Self::U16Pair),
|
||||
((u16, u16, u16), Self::U16Triplet),
|
||||
(i16, Self::I16),
|
||||
((i16, i16), Self::I16Pair),
|
||||
((i16, i16, i16), Self::I16Triplet),
|
||||
(u32, Self::U32),
|
||||
((u32, u32), Self::U32Pair),
|
||||
((u32, u32, u32), Self::U32Triplet),
|
||||
(i32, Self::I32),
|
||||
((i32, i32), Self::I32Pair),
|
||||
((i32, i32, i32), Self::I32Triplet),
|
||||
(f32, Self::F32),
|
||||
((f32, f32), Self::F32Pair),
|
||||
((f32, f32, f32), Self::F32Triplet),
|
||||
(u64, Self::U64),
|
||||
(f64, Self::F64),
|
||||
);
|
||||
|
||||
/// Generic enumeration for additional parameters, including parameters which rely on heap
|
||||
/// allocations.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Params {
|
||||
Heapless(ParamsHeapless),
|
||||
Vec(Vec<u8>),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl From<ParamsHeapless> for Params {
|
||||
fn from(x: ParamsHeapless) -> Self {
|
||||
Self::Heapless(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Params {
|
||||
fn from(val: Vec<u8>) -> Self {
|
||||
Self::Vec(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a byte slice into the [Params::Vec] variant
|
||||
impl From<&[u8]> for Params {
|
||||
fn from(val: &[u8]) -> Self {
|
||||
Self::Vec(val.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Params {
|
||||
fn from(val: String) -> Self {
|
||||
Self::String(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a string slice into the [Params::String] variant
|
||||
impl From<&str> for Params {
|
||||
fn from(val: &str) -> Self {
|
||||
Self::String(val.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_basic_u32_pair() {
|
||||
let u32_pair = U32Pair(4, 8);
|
||||
assert_eq!(u32_pair.0, 4);
|
||||
assert_eq!(u32_pair.1, 8);
|
||||
let raw = u32_pair.to_be_bytes();
|
||||
let mut u32_conv_back = u32::from_be_bytes(raw[0..4].try_into().unwrap());
|
||||
assert_eq!(u32_conv_back, 4);
|
||||
u32_conv_back = u32::from_be_bytes(raw[4..8].try_into().unwrap());
|
||||
assert_eq!(u32_conv_back, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_signed_test_pair() {
|
||||
let i8_pair = I8Pair(-3, -16);
|
||||
assert_eq!(i8_pair.0, -3);
|
||||
assert_eq!(i8_pair.1, -16);
|
||||
let raw = i8_pair.to_be_bytes();
|
||||
let mut i8_conv_back = i8::from_be_bytes(raw[0..1].try_into().unwrap());
|
||||
assert_eq!(i8_conv_back, -3);
|
||||
i8_conv_back = i8::from_be_bytes(raw[1..2].try_into().unwrap());
|
||||
assert_eq!(i8_conv_back, -16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_signed_test_triplet() {
|
||||
let i8_triplet = I8Triplet(-3, -16, -126);
|
||||
assert_eq!(i8_triplet.0, -3);
|
||||
assert_eq!(i8_triplet.1, -16);
|
||||
assert_eq!(i8_triplet.2, -126);
|
||||
let raw = i8_triplet.to_be_bytes();
|
||||
let mut i8_conv_back = i8::from_be_bytes(raw[0..1].try_into().unwrap());
|
||||
assert_eq!(i8_conv_back, -3);
|
||||
i8_conv_back = i8::from_be_bytes(raw[1..2].try_into().unwrap());
|
||||
assert_eq!(i8_conv_back, -16);
|
||||
i8_conv_back = i8::from_be_bytes(raw[2..3].try_into().unwrap());
|
||||
assert_eq!(i8_conv_back, -126);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conversion_test_string() {
|
||||
let param: Params = "Test String".into();
|
||||
if let Params::String(str) = param {
|
||||
assert_eq!(str, String::from("Test String"));
|
||||
} else {
|
||||
panic!("Params type is not String")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conversion_from_slice() {
|
||||
let test_slice: [u8; 5] = [0; 5];
|
||||
let vec_param: Params = test_slice.as_slice().into();
|
||||
if let Params::Vec(vec) = vec_param {
|
||||
assert_eq!(vec, test_slice.to_vec());
|
||||
} else {
|
||||
panic!("Params type is not a vector")
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,37 @@ impl From<Subservices> for u8 {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Subservices {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == Subservices::TmInfoReport as u8 => Ok(Subservices::TmInfoReport),
|
||||
x if x == Subservices::TmLowSeverityReport as u8 => {
|
||||
Ok(Subservices::TmLowSeverityReport)
|
||||
}
|
||||
x if x == Subservices::TmMediumSeverityReport as u8 => {
|
||||
Ok(Subservices::TmMediumSeverityReport)
|
||||
}
|
||||
x if x == Subservices::TmHighSeverityReport as u8 => {
|
||||
Ok(Subservices::TmHighSeverityReport)
|
||||
}
|
||||
x if x == Subservices::TcEnableEventGeneration as u8 => {
|
||||
Ok(Subservices::TcEnableEventGeneration)
|
||||
}
|
||||
x if x == Subservices::TcDisableEventGeneration as u8 => {
|
||||
Ok(Subservices::TcDisableEventGeneration)
|
||||
}
|
||||
x if x == Subservices::TcReportDisabledList as u8 => {
|
||||
Ok(Subservices::TcReportDisabledList)
|
||||
}
|
||||
x if x == Subservices::TmDisabledEventsReport as u8 => {
|
||||
Ok(Subservices::TmDisabledEventsReport)
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct EventReporterBase {
|
||||
msg_count: u16,
|
||||
apid: u16,
|
||||
@ -46,7 +77,7 @@ impl EventReporterBase {
|
||||
pub fn event_info<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -64,7 +95,7 @@ impl EventReporterBase {
|
||||
pub fn event_low_severity<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -82,7 +113,7 @@ impl EventReporterBase {
|
||||
pub fn event_medium_severity<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -100,7 +131,7 @@ impl EventReporterBase {
|
||||
pub fn event_high_severity<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -119,7 +150,7 @@ impl EventReporterBase {
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
subservice: Subservices,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -152,7 +183,7 @@ impl EventReporterBase {
|
||||
time_stamp,
|
||||
);
|
||||
let mut current_idx = 0;
|
||||
event_id.write_to_bytes(&mut buf[0..event_id.byte_width()])?;
|
||||
event_id.write_to_be_bytes(&mut buf[0..event_id.byte_width()])?;
|
||||
current_idx += event_id.byte_width();
|
||||
if let Some(aux_data) = aux_data {
|
||||
buf[current_idx..current_idx + aux_data.len()].copy_from_slice(aux_data);
|
||||
@ -188,7 +219,7 @@ mod allocvec {
|
||||
}
|
||||
pub fn event_info<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -204,7 +235,7 @@ mod allocvec {
|
||||
|
||||
pub fn event_low_severity<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -220,7 +251,7 @@ mod allocvec {
|
||||
|
||||
pub fn event_medium_severity<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -236,7 +267,7 @@ mod allocvec {
|
||||
|
||||
pub fn event_high_severity<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event_id: impl EcssEnumeration,
|
||||
aux_data: Option<&[u8]>,
|
||||
@ -255,7 +286,7 @@ mod allocvec {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::events::{Event, Severity};
|
||||
use crate::events::{EventU32, Severity};
|
||||
use crate::pus::tests::CommonTmInfo;
|
||||
use spacepackets::ByteConversionError;
|
||||
use std::collections::VecDeque;
|
||||
@ -270,7 +301,7 @@ mod tests {
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct TmInfo {
|
||||
pub common: CommonTmInfo,
|
||||
pub event: Event,
|
||||
pub event: EventU32,
|
||||
pub aux_data: Vec<u8>,
|
||||
}
|
||||
|
||||
@ -279,14 +310,14 @@ mod tests {
|
||||
pub service_queue: VecDeque<TmInfo>,
|
||||
}
|
||||
|
||||
impl EcssTmSender<()> for TestSender {
|
||||
impl EcssTmSender for TestSender {
|
||||
type Error = ();
|
||||
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<()>> {
|
||||
assert!(tm.source_data().is_some());
|
||||
let src_data = tm.source_data().unwrap();
|
||||
assert!(src_data.len() >= 4);
|
||||
let event = Event::try_from(u32::from_be_bytes(src_data[0..4].try_into().unwrap()));
|
||||
assert!(event.is_ok());
|
||||
let event = event.unwrap();
|
||||
let event = EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap()));
|
||||
let mut aux_data = Vec::new();
|
||||
if src_data.len() > 4 {
|
||||
aux_data.extend_from_slice(&src_data[4..]);
|
||||
@ -313,7 +344,7 @@ mod tests {
|
||||
reporter: &mut EventReporter,
|
||||
sender: &mut TestSender,
|
||||
time_stamp: &[u8],
|
||||
event: Event,
|
||||
event: EventU32,
|
||||
severity: Severity,
|
||||
aux_data: Option<&[u8]>,
|
||||
) {
|
||||
@ -355,7 +386,7 @@ mod tests {
|
||||
if let Some(err_data) = error_data {
|
||||
error_copy.extend_from_slice(err_data);
|
||||
}
|
||||
let event = Event::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
|
||||
let event = EventU32::new(severity, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
|
||||
.expect("Error creating example event");
|
||||
report_basic_event(
|
||||
&mut reporter,
|
||||
@ -418,7 +449,7 @@ mod tests {
|
||||
expected_found_len: usize,
|
||||
) {
|
||||
let time_stamp_empty: [u8; 7] = [0; 7];
|
||||
let event = Event::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
|
||||
let event = EventU32::new(Severity::INFO, EXAMPLE_GROUP_ID, EXAMPLE_EVENT_ID_0)
|
||||
.expect("Error creating example event");
|
||||
let err = reporter.event_info(sender, &time_stamp_empty, event, None);
|
||||
assert!(err.is_err());
|
||||
|
@ -1,20 +1,28 @@
|
||||
use crate::events::EventProvider;
|
||||
use crate::events::{EventU32, EventU32TypedSev, GenericEvent, HasSeverity, Severity};
|
||||
use alloc::boxed::Box;
|
||||
use core::hash::Hash;
|
||||
use hashbrown::HashSet;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use crate::pus::event::EventReporter;
|
||||
use crate::pus::verification::{TcStateStarted, VerificationToken};
|
||||
use crate::pus::{EcssTmError, EcssTmSender};
|
||||
#[cfg(feature = "heapless")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
|
||||
pub use heapless_mod::*;
|
||||
|
||||
/// This trait allows the PUS event manager implementation to stay generic over various types
|
||||
/// of backend containers. These backend containers keep track on whether a particular event
|
||||
/// is enabled or disabled for reporting and also expose a simple API to enable or disable the event
|
||||
/// reporting.
|
||||
/// of backend containers.
|
||||
///
|
||||
/// These backend containers keep track on whether a particular event is enabled or disabled for
|
||||
/// reporting and also expose a simple API to enable or disable the event reporting.
|
||||
///
|
||||
/// For example, a straight forward implementation for host systems could use a
|
||||
/// [hash set](https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html)
|
||||
/// structure to track disabled events. A more primitive and embedded friendly
|
||||
/// solution could track this information in a static or pre-allocated list which contains
|
||||
/// the disabled events.
|
||||
pub trait PusEventMgmtBackendProvider<Provider: EventProvider> {
|
||||
pub trait PusEventMgmtBackendProvider<Provider: GenericEvent> {
|
||||
type Error;
|
||||
|
||||
fn event_enabled(&self, event: &Provider) -> bool;
|
||||
@ -27,13 +35,23 @@ pub trait PusEventMgmtBackendProvider<Provider: EventProvider> {
|
||||
///
|
||||
/// This provider is a good option for host systems or larger embedded systems where
|
||||
/// the expected occasional memory allocation performed by the [HashSet] is not an issue.
|
||||
#[derive(Default)]
|
||||
pub struct DefaultPusMgmtBackendProvider<Provider: EventProvider> {
|
||||
disabled: HashSet<Provider>,
|
||||
pub struct DefaultPusMgmtBackendProvider<Event: GenericEvent = EventU32> {
|
||||
disabled: HashSet<Event>,
|
||||
}
|
||||
|
||||
impl<Provider: EventProvider> PusEventMgmtBackendProvider<Provider>
|
||||
for DefaultPusMgmtBackendProvider<Provider>
|
||||
/// Safety: All contained field are [Send] as well
|
||||
unsafe impl<Event: GenericEvent + Send> Send for DefaultPusMgmtBackendProvider<Event> {}
|
||||
|
||||
impl<Event: GenericEvent> Default for DefaultPusMgmtBackendProvider<Event> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
disabled: HashSet::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider: GenericEvent + PartialEq + Eq + Hash + Copy + Clone>
|
||||
PusEventMgmtBackendProvider<Provider> for DefaultPusMgmtBackendProvider<Provider>
|
||||
{
|
||||
type Error = ();
|
||||
fn event_enabled(&self, event: &Provider) -> bool {
|
||||
@ -52,19 +70,26 @@ impl<Provider: EventProvider> PusEventMgmtBackendProvider<Provider>
|
||||
#[cfg(feature = "heapless")]
|
||||
pub mod heapless_mod {
|
||||
use super::*;
|
||||
use crate::events::{EventProvider, LargestEventRaw};
|
||||
use crate::events::{GenericEvent, LargestEventRaw};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "heapless")))]
|
||||
// TODO: After a new version of heapless is released which uses hash32 version 0.3, try using
|
||||
// regular Event type again.
|
||||
#[derive(Default)]
|
||||
pub struct HeaplessPusMgmtBckendProvider<const N: usize, Provider: EventProvider> {
|
||||
pub struct HeaplessPusMgmtBackendProvider<const N: usize, Provider: GenericEvent> {
|
||||
disabled: heapless::FnvIndexSet<LargestEventRaw, N>,
|
||||
phantom: PhantomData<Provider>,
|
||||
}
|
||||
|
||||
impl<const N: usize, Provider: EventProvider> PusEventMgmtBackendProvider<Provider>
|
||||
for HeaplessPusMgmtBckendProvider<N, Provider>
|
||||
/// Safety: All contained field are [Send] as well
|
||||
unsafe impl<const N: usize, Event: GenericEvent + Send> Send
|
||||
for HeaplessPusMgmtBackendProvider<N, Event>
|
||||
{
|
||||
}
|
||||
|
||||
impl<const N: usize, Provider: GenericEvent> PusEventMgmtBackendProvider<Provider>
|
||||
for HeaplessPusMgmtBackendProvider<N, Provider>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
@ -84,4 +109,202 @@ pub mod heapless_mod {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PusEventManager {}
|
||||
#[derive(Debug)]
|
||||
pub enum EventRequest<Event: GenericEvent = EventU32> {
|
||||
Enable(Event),
|
||||
Disable(Event),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventRequestWithToken<Event: GenericEvent = EventU32> {
|
||||
pub request: EventRequest<Event>,
|
||||
pub token: VerificationToken<TcStateStarted>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventManError<SenderE> {
|
||||
EcssTmError(EcssTmError<SenderE>),
|
||||
SeverityMissmatch(Severity, Severity),
|
||||
}
|
||||
|
||||
impl<SenderE> From<EcssTmError<SenderE>> for EventManError<SenderE> {
|
||||
fn from(v: EcssTmError<SenderE>) -> Self {
|
||||
Self::EcssTmError(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PusEventDispatcher<BackendError, Provider: GenericEvent> {
|
||||
reporter: EventReporter,
|
||||
backend: Box<dyn PusEventMgmtBackendProvider<Provider, Error = BackendError>>,
|
||||
}
|
||||
|
||||
/// Safety: All contained fields are send as well.
|
||||
unsafe impl<E: Send, Event: GenericEvent + Send> Send for PusEventDispatcher<E, Event> {}
|
||||
|
||||
impl<BackendError, Provider: GenericEvent> PusEventDispatcher<BackendError, Provider> {
|
||||
pub fn new(
|
||||
reporter: EventReporter,
|
||||
backend: Box<dyn PusEventMgmtBackendProvider<Provider, Error = BackendError>>,
|
||||
) -> Self {
|
||||
Self { reporter, backend }
|
||||
}
|
||||
}
|
||||
|
||||
impl<BackendError, Event: GenericEvent> PusEventDispatcher<BackendError, Event> {
|
||||
pub fn enable_tm_for_event(&mut self, event: &Event) -> Result<bool, BackendError> {
|
||||
self.backend.enable_event_reporting(event)
|
||||
}
|
||||
|
||||
pub fn disable_tm_for_event(&mut self, event: &Event) -> Result<bool, BackendError> {
|
||||
self.backend.disable_event_reporting(event)
|
||||
}
|
||||
|
||||
pub fn generate_pus_event_tm_generic<E>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event: Event,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<bool, EventManError<E>> {
|
||||
if !self.backend.event_enabled(&event) {
|
||||
return Ok(false);
|
||||
}
|
||||
match event.severity() {
|
||||
Severity::INFO => self
|
||||
.reporter
|
||||
.event_info(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
Severity::LOW => self
|
||||
.reporter
|
||||
.event_low_severity(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
Severity::MEDIUM => self
|
||||
.reporter
|
||||
.event_medium_severity(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
Severity::HIGH => self
|
||||
.reporter
|
||||
.event_high_severity(sender, time_stamp, event, aux_data)
|
||||
.map(|_| true)
|
||||
.map_err(|e| e.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<BackendError> PusEventDispatcher<BackendError, EventU32> {
|
||||
pub fn enable_tm_for_event_with_sev<Severity: HasSeverity>(
|
||||
&mut self,
|
||||
event: &EventU32TypedSev<Severity>,
|
||||
) -> Result<bool, BackendError> {
|
||||
self.backend.enable_event_reporting(event.as_ref())
|
||||
}
|
||||
|
||||
pub fn disable_tm_for_event_with_sev<Severity: HasSeverity>(
|
||||
&mut self,
|
||||
event: &EventU32TypedSev<Severity>,
|
||||
) -> Result<bool, BackendError> {
|
||||
self.backend.disable_event_reporting(event.as_ref())
|
||||
}
|
||||
|
||||
pub fn generate_pus_event_tm<E, Severity: HasSeverity>(
|
||||
&mut self,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
event: EventU32TypedSev<Severity>,
|
||||
aux_data: Option<&[u8]>,
|
||||
) -> Result<bool, EventManError<E>> {
|
||||
self.generate_pus_event_tm_generic(sender, time_stamp, event.into(), aux_data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::events::SeverityInfo;
|
||||
use spacepackets::tm::PusTm;
|
||||
use std::sync::mpsc::{channel, SendError, TryRecvError};
|
||||
use std::vec::Vec;
|
||||
|
||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> =
|
||||
EventU32TypedSev::<SeverityInfo>::const_new(1, 0);
|
||||
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
|
||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
|
||||
struct EventTmSender {
|
||||
sender: std::sync::mpsc::Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl EcssTmSender for EventTmSender {
|
||||
type Error = SendError<Vec<u8>>;
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>> {
|
||||
let mut vec = Vec::new();
|
||||
tm.append_to_vec(&mut vec)?;
|
||||
self.sender.send(vec).map_err(EcssTmError::SendError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_basic_man() -> PusEventDispatcher<(), EventU32> {
|
||||
let reporter = EventReporter::new(0x02, 128).expect("Creating event repoter failed");
|
||||
let backend = DefaultPusMgmtBackendProvider::<EventU32>::default();
|
||||
PusEventDispatcher::new(reporter, Box::new(backend))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let mut event_man = create_basic_man();
|
||||
let (event_tx, event_rx) = channel();
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let event_sent = event_man
|
||||
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
|
||||
.expect("Sending info event failed");
|
||||
|
||||
assert!(event_sent);
|
||||
// Will not check packet here, correctness of packet was tested somewhere else
|
||||
event_rx.try_recv().expect("Receiving event TM failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_disable_event() {
|
||||
let mut event_man = create_basic_man();
|
||||
let (event_tx, event_rx) = channel();
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let res = event_man.disable_tm_for_event(&LOW_SEV_EVENT);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let mut event_sent = event_man
|
||||
.generate_pus_event_tm_generic(&mut sender, &EMPTY_STAMP, LOW_SEV_EVENT, None)
|
||||
.expect("Sending low severity event failed");
|
||||
assert!(!event_sent);
|
||||
let res = event_rx.try_recv();
|
||||
assert!(res.is_err());
|
||||
assert!(matches!(res.unwrap_err(), TryRecvError::Empty));
|
||||
// Check that only the low severity event was disabled
|
||||
event_sent = event_man
|
||||
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
|
||||
.expect("Sending info event failed");
|
||||
assert!(event_sent);
|
||||
event_rx.try_recv().expect("No info event received");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reenable_event() {
|
||||
let mut event_man = create_basic_man();
|
||||
let (event_tx, event_rx) = channel();
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let mut res = event_man.disable_tm_for_event_with_sev(&INFO_EVENT);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
res = event_man.enable_tm_for_event_with_sev(&INFO_EVENT);
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
let event_sent = event_man
|
||||
.generate_pus_event_tm(&mut sender, &EMPTY_STAMP, INFO_EVENT, None)
|
||||
.expect("Sending info event failed");
|
||||
assert!(event_sent);
|
||||
event_rx.try_recv().expect("No info event received");
|
||||
}
|
||||
}
|
||||
|
@ -26,21 +26,30 @@ pub enum EcssTmError<E> {
|
||||
PusError(PusError),
|
||||
}
|
||||
|
||||
impl<E> From<PusError> for EcssTmError<E> {
|
||||
fn from(e: PusError) -> Self {
|
||||
EcssTmError::PusError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<ByteConversionError> for EcssTmError<E> {
|
||||
fn from(e: ByteConversionError) -> Self {
|
||||
EcssTmError::ByteConversionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic trait for a user supplied sender object. This sender object is responsible for sending
|
||||
/// telemetry to a TM sink. The [Downcast] trait
|
||||
/// Generic trait for a user supplied sender object.
|
||||
///
|
||||
/// This sender object is responsible for sending telemetry to a TM sink. The [Downcast] trait
|
||||
/// is implemented to allow passing the sender as a boxed trait object and still retrieve the
|
||||
/// concrete type at a later point.
|
||||
pub trait EcssTmSender<E>: Downcast + Send {
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<E>>;
|
||||
pub trait EcssTmSender: Downcast + Send {
|
||||
type Error;
|
||||
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>>;
|
||||
}
|
||||
|
||||
impl_downcast!(EcssTmSender<E>);
|
||||
impl_downcast!(EcssTmSender assoc Error);
|
||||
|
||||
pub(crate) fn source_buffer_large_enough<E>(cap: usize, len: usize) -> Result<(), EcssTmError<E>> {
|
||||
if len > cap {
|
||||
|
@ -191,20 +191,39 @@ pub struct VerificationToken<STATE> {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct StateNone;
|
||||
pub struct TcStateNone;
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct StateAccepted;
|
||||
pub struct TcStateAccepted;
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct StateStarted;
|
||||
pub struct TcStateStarted;
|
||||
|
||||
pub enum StateToken {
|
||||
None(StateNone),
|
||||
Accepted(StateAccepted),
|
||||
Started(StateStarted),
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum TcStateToken {
|
||||
None(VerificationToken<TcStateNone>),
|
||||
Accepted(VerificationToken<TcStateAccepted>),
|
||||
Started(VerificationToken<TcStateStarted>),
|
||||
}
|
||||
|
||||
impl From<VerificationToken<TcStateNone>> for TcStateToken {
|
||||
fn from(t: VerificationToken<TcStateNone>) -> Self {
|
||||
TcStateToken::None(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerificationToken<TcStateAccepted>> for TcStateToken {
|
||||
fn from(t: VerificationToken<TcStateAccepted>) -> Self {
|
||||
TcStateToken::Accepted(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerificationToken<TcStateStarted>> for TcStateToken {
|
||||
fn from(t: VerificationToken<TcStateStarted>) -> Self {
|
||||
TcStateToken::Started(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<STATE> VerificationToken<STATE> {
|
||||
fn new(req_id: RequestId) -> VerificationToken<StateNone> {
|
||||
fn new(req_id: RequestId) -> VerificationToken<TcStateNone> {
|
||||
VerificationToken {
|
||||
state: PhantomData,
|
||||
req_id,
|
||||
@ -297,24 +316,25 @@ impl VerificationReporterBasic {
|
||||
|
||||
/// Initialize verification handling by passing a TC reference. This returns a token required
|
||||
/// to call the acceptance functions
|
||||
pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken<StateNone> {
|
||||
pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken<TcStateNone> {
|
||||
self.add_tc_with_req_id(RequestId::new(pus_tc))
|
||||
}
|
||||
|
||||
/// Same as [Self::add_tc] but pass a request ID instead of the direct telecommand.
|
||||
/// This can be useful if the executing thread does not have full access to the telecommand.
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<StateNone> {
|
||||
VerificationToken::<StateNone>::new(req_id)
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone> {
|
||||
VerificationToken::<TcStateNone>::new(req_id)
|
||||
}
|
||||
|
||||
/// Package and send a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard
|
||||
pub fn acceptance_success<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateNone>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateNone>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<StateAccepted>, VerificationErrorWithToken<E, StateNone>> {
|
||||
) -> Result<VerificationToken<TcStateAccepted>, VerificationErrorWithToken<E, TcStateNone>>
|
||||
{
|
||||
let tm = self
|
||||
.create_pus_verif_success_tm(
|
||||
buf,
|
||||
@ -338,10 +358,10 @@ impl VerificationReporterBasic {
|
||||
pub fn acceptance_failure<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateNone>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateNone>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateNone>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateNone>> {
|
||||
let tm = self
|
||||
.create_pus_verif_fail_tm(
|
||||
buf,
|
||||
@ -364,10 +384,11 @@ impl VerificationReporterBasic {
|
||||
pub fn start_success<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<StateStarted>, VerificationErrorWithToken<E, StateAccepted>> {
|
||||
) -> Result<VerificationToken<TcStateStarted>, VerificationErrorWithToken<E, TcStateAccepted>>
|
||||
{
|
||||
let tm = self
|
||||
.create_pus_verif_success_tm(
|
||||
buf,
|
||||
@ -394,10 +415,10 @@ impl VerificationReporterBasic {
|
||||
pub fn start_failure<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateAccepted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateAccepted>> {
|
||||
let tm = self
|
||||
.create_pus_verif_fail_tm(
|
||||
buf,
|
||||
@ -420,8 +441,8 @@ impl VerificationReporterBasic {
|
||||
pub fn step_success<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: &VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
@ -444,10 +465,10 @@ impl VerificationReporterBasic {
|
||||
pub fn step_failure<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParamsWithStep,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
let tm = self
|
||||
.create_pus_verif_fail_tm(
|
||||
buf,
|
||||
@ -471,10 +492,10 @@ impl VerificationReporterBasic {
|
||||
pub fn completion_success<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
let tm = self
|
||||
.create_pus_verif_success_tm(
|
||||
buf,
|
||||
@ -498,10 +519,10 @@ impl VerificationReporterBasic {
|
||||
pub fn completion_failure<E>(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
token: VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
let tm = self
|
||||
.create_pus_verif_fail_tm(
|
||||
buf,
|
||||
@ -536,7 +557,7 @@ impl VerificationReporterBasic {
|
||||
idx += RequestId::SIZE_AS_BYTES;
|
||||
if let Some(step) = step {
|
||||
// Size check was done beforehand
|
||||
step.write_to_bytes(&mut buf[idx..idx + step.byte_width() as usize])
|
||||
step.write_to_be_bytes(&mut buf[idx..idx + step.byte_width() as usize])
|
||||
.unwrap();
|
||||
}
|
||||
let mut sp_header = SpHeader::tm(self.apid(), 0, 0).unwrap();
|
||||
@ -571,13 +592,13 @@ impl VerificationReporterBasic {
|
||||
idx += RequestId::SIZE_AS_BYTES;
|
||||
if let Some(step) = step {
|
||||
// Size check done beforehand
|
||||
step.write_to_bytes(&mut buf[idx..idx + step.byte_width() as usize])
|
||||
step.write_to_be_bytes(&mut buf[idx..idx + step.byte_width() as usize])
|
||||
.unwrap();
|
||||
idx += step.byte_width() as usize;
|
||||
}
|
||||
params
|
||||
.failure_code
|
||||
.write_to_bytes(&mut buf[idx..idx + params.failure_code.byte_width() as usize])?;
|
||||
.write_to_be_bytes(&mut buf[idx..idx + params.failure_code.byte_width() as usize])?;
|
||||
idx += params.failure_code.byte_width() as usize;
|
||||
if let Some(failure_data) = params.failure_data {
|
||||
buf[idx..idx + failure_data.len()].copy_from_slice(failure_data);
|
||||
@ -670,8 +691,8 @@ mod allocmod {
|
||||
to self.reporter {
|
||||
pub fn set_apid(&mut self, apid: u16) -> bool;
|
||||
pub fn apid(&self) -> u16;
|
||||
pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken<StateNone>;
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<StateNone>;
|
||||
pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken<TcStateNone>;
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
|
||||
pub fn dest_id(&self) -> u16;
|
||||
pub fn set_dest_id(&mut self, dest_id: u16);
|
||||
}
|
||||
@ -684,10 +705,10 @@ mod allocmod {
|
||||
/// Package and send a PUS TM\[1, 1\] packet, see 8.1.2.1 of the PUS standard
|
||||
pub fn acceptance_success<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateNone>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateNone>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<StateAccepted>, VerificationErrorWithToken<E, StateNone>>
|
||||
) -> Result<VerificationToken<TcStateAccepted>, VerificationErrorWithToken<E, TcStateNone>>
|
||||
{
|
||||
self.reporter.acceptance_success(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
@ -700,10 +721,10 @@ mod allocmod {
|
||||
/// Package and send a PUS TM\[1, 2\] packet, see 8.1.2.2 of the PUS standard
|
||||
pub fn acceptance_failure<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateNone>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateNone>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateNone>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateNone>> {
|
||||
self.reporter.acceptance_failure(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
token,
|
||||
@ -717,10 +738,10 @@ mod allocmod {
|
||||
/// Requires a token previously acquired by calling [Self::acceptance_success].
|
||||
pub fn start_success<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<StateStarted>, VerificationErrorWithToken<E, StateAccepted>>
|
||||
) -> Result<VerificationToken<TcStateStarted>, VerificationErrorWithToken<E, TcStateAccepted>>
|
||||
{
|
||||
self.reporter.start_success(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
@ -736,10 +757,10 @@ mod allocmod {
|
||||
/// the token because verification handling is done.
|
||||
pub fn start_failure<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateAccepted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateAccepted>> {
|
||||
self.reporter
|
||||
.start_failure(self.source_data_buf.as_mut_slice(), token, sender, params)
|
||||
}
|
||||
@ -749,8 +770,8 @@ mod allocmod {
|
||||
/// Requires a token previously acquired by calling [Self::start_success].
|
||||
pub fn step_success<E>(
|
||||
&mut self,
|
||||
token: &VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
@ -769,10 +790,10 @@ mod allocmod {
|
||||
/// token because verification handling is done.
|
||||
pub fn step_failure<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParamsWithStep,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
self.reporter
|
||||
.step_failure(self.source_data_buf.as_mut_slice(), token, sender, params)
|
||||
}
|
||||
@ -783,10 +804,10 @@ mod allocmod {
|
||||
/// token because verification handling is done.
|
||||
pub fn completion_success<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
self.reporter.completion_success(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
token,
|
||||
@ -801,10 +822,10 @@ mod allocmod {
|
||||
/// token because verification handling is done.
|
||||
pub fn completion_failure<E>(
|
||||
&mut self,
|
||||
token: VerificationToken<StateStarted>,
|
||||
sender: &mut (impl EcssTmSender<E> + ?Sized),
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
sender: &mut (impl EcssTmSender<Error = E> + ?Sized),
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
self.reporter.completion_failure(
|
||||
self.source_data_buf.as_mut_slice(),
|
||||
token,
|
||||
@ -818,18 +839,18 @@ mod allocmod {
|
||||
/// API as [VerificationReporter] but without the explicit sender arguments.
|
||||
pub struct VerificationReporterWithSender<E> {
|
||||
pub reporter: VerificationReporter,
|
||||
pub sender: Box<dyn EcssTmSender<E>>,
|
||||
pub sender: Box<dyn EcssTmSender<Error = E>>,
|
||||
}
|
||||
|
||||
impl<E: 'static> VerificationReporterWithSender<E> {
|
||||
pub fn new(cfg: VerificationReporterCfg, sender: Box<dyn EcssTmSender<E>>) -> Self {
|
||||
pub fn new(cfg: VerificationReporterCfg, sender: Box<dyn EcssTmSender<Error = E>>) -> Self {
|
||||
let reporter = VerificationReporter::new(cfg);
|
||||
Self::new_from_reporter(reporter, sender)
|
||||
}
|
||||
|
||||
pub fn new_from_reporter(
|
||||
reporter: VerificationReporter,
|
||||
sender: Box<dyn EcssTmSender<E>>,
|
||||
sender: Box<dyn EcssTmSender<Error = E>>,
|
||||
) -> Self {
|
||||
Self { reporter, sender }
|
||||
}
|
||||
@ -838,8 +859,8 @@ mod allocmod {
|
||||
to self.reporter {
|
||||
pub fn set_apid(&mut self, apid: u16) -> bool;
|
||||
pub fn apid(&self) -> u16;
|
||||
pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken<StateNone>;
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<StateNone>;
|
||||
pub fn add_tc(&mut self, pus_tc: &PusTc) -> VerificationToken<TcStateNone>;
|
||||
pub fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
|
||||
pub fn dest_id(&self) -> u16;
|
||||
pub fn set_dest_id(&mut self, dest_id: u16);
|
||||
}
|
||||
@ -847,9 +868,9 @@ mod allocmod {
|
||||
|
||||
pub fn acceptance_success(
|
||||
&mut self,
|
||||
token: VerificationToken<StateNone>,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<StateAccepted>, VerificationErrorWithToken<E, StateNone>>
|
||||
) -> Result<VerificationToken<TcStateAccepted>, VerificationErrorWithToken<E, TcStateNone>>
|
||||
{
|
||||
self.reporter
|
||||
.acceptance_success(token, self.sender.as_mut(), time_stamp)
|
||||
@ -857,18 +878,18 @@ mod allocmod {
|
||||
|
||||
pub fn acceptance_failure(
|
||||
&mut self,
|
||||
token: VerificationToken<StateNone>,
|
||||
token: VerificationToken<TcStateNone>,
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateNone>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateNone>> {
|
||||
self.reporter
|
||||
.acceptance_failure(token, self.sender.as_mut(), params)
|
||||
}
|
||||
|
||||
pub fn start_success(
|
||||
&mut self,
|
||||
token: VerificationToken<StateAccepted>,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<VerificationToken<StateStarted>, VerificationErrorWithToken<E, StateAccepted>>
|
||||
) -> Result<VerificationToken<TcStateStarted>, VerificationErrorWithToken<E, TcStateAccepted>>
|
||||
{
|
||||
self.reporter
|
||||
.start_success(token, self.sender.as_mut(), time_stamp)
|
||||
@ -876,16 +897,16 @@ mod allocmod {
|
||||
|
||||
pub fn start_failure(
|
||||
&mut self,
|
||||
token: VerificationToken<StateAccepted>,
|
||||
token: VerificationToken<TcStateAccepted>,
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateAccepted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateAccepted>> {
|
||||
self.reporter
|
||||
.start_failure(token, self.sender.as_mut(), params)
|
||||
}
|
||||
|
||||
pub fn step_success(
|
||||
&mut self,
|
||||
token: &VerificationToken<StateStarted>,
|
||||
token: &VerificationToken<TcStateStarted>,
|
||||
time_stamp: &[u8],
|
||||
step: impl EcssEnumeration,
|
||||
) -> Result<(), EcssTmError<E>> {
|
||||
@ -895,27 +916,27 @@ mod allocmod {
|
||||
|
||||
pub fn step_failure(
|
||||
&mut self,
|
||||
token: VerificationToken<StateStarted>,
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
params: FailParamsWithStep,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
self.reporter
|
||||
.step_failure(token, self.sender.as_mut(), params)
|
||||
}
|
||||
|
||||
pub fn completion_success(
|
||||
&mut self,
|
||||
token: VerificationToken<StateStarted>,
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
time_stamp: &[u8],
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
self.reporter
|
||||
.completion_success(token, self.sender.as_mut(), time_stamp)
|
||||
}
|
||||
|
||||
pub fn completion_failure(
|
||||
&mut self,
|
||||
token: VerificationToken<StateStarted>,
|
||||
token: VerificationToken<TcStateStarted>,
|
||||
params: FailParams,
|
||||
) -> Result<(), VerificationErrorWithToken<E, StateStarted>> {
|
||||
) -> Result<(), VerificationErrorWithToken<E, TcStateStarted>> {
|
||||
self.reporter
|
||||
.completion_failure(token, self.sender.as_mut(), params)
|
||||
}
|
||||
@ -987,7 +1008,7 @@ mod stdmod {
|
||||
}
|
||||
|
||||
/// Verification sender with a [mpsc::Sender] backend.
|
||||
/// It implements the [VerificationSender] trait to be used as PUS Verification TM sender.
|
||||
/// It implements the [EcssTmSender] trait to be used as PUS Verification TM sender.
|
||||
impl MpscVerifSender {
|
||||
pub fn new(tm_store: SharedPool, tx: mpsc::Sender<StoreAddr>) -> Self {
|
||||
Self {
|
||||
@ -997,7 +1018,9 @@ mod stdmod {
|
||||
}
|
||||
|
||||
//noinspection RsTraitImplementation
|
||||
impl EcssTmSender<StdVerifSenderError> for MpscVerifSender {
|
||||
impl EcssTmSender for MpscVerifSender {
|
||||
type Error = StdVerifSenderError;
|
||||
|
||||
delegate!(
|
||||
to self.base {
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<StdVerifSenderError>>;
|
||||
@ -1014,7 +1037,7 @@ mod stdmod {
|
||||
}
|
||||
|
||||
/// Verification sender with a [crossbeam_channel::Sender] backend.
|
||||
/// It implements the [VerificationSender] trait to be used as PUS Verification TM sender
|
||||
/// It implements the [EcssTmSender] trait to be used as PUS Verification TM sender
|
||||
pub struct CrossbeamVerifSender {
|
||||
base: StdSenderBase<crossbeam_channel::Sender<StoreAddr>>,
|
||||
}
|
||||
@ -1028,7 +1051,9 @@ mod stdmod {
|
||||
}
|
||||
|
||||
//noinspection RsTraitImplementation
|
||||
impl EcssTmSender<StdVerifSenderError> for CrossbeamVerifSender {
|
||||
impl EcssTmSender for CrossbeamVerifSender {
|
||||
type Error = StdVerifSenderError;
|
||||
|
||||
delegate!(
|
||||
to self.base {
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<StdVerifSenderError>>;
|
||||
@ -1039,8 +1064,9 @@ mod stdmod {
|
||||
unsafe impl Sync for CrossbeamVerifSender {}
|
||||
unsafe impl Send for CrossbeamVerifSender {}
|
||||
|
||||
impl<S: SendBackend + 'static> EcssTmSender<StdVerifSenderError> for StdSenderBase<S> {
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<StdVerifSenderError>> {
|
||||
impl<S: SendBackend + 'static> EcssTmSender for StdSenderBase<S> {
|
||||
type Error = StdVerifSenderError;
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>> {
|
||||
let operation = |mut mg: RwLockWriteGuard<ShareablePoolProvider>| {
|
||||
let (addr, buf) = mg.free_element(tm.len_packed())?;
|
||||
tm.write_to_bytes(buf).map_err(EcssTmError::PusError)?;
|
||||
@ -1068,7 +1094,7 @@ mod stdmod {
|
||||
mod tests {
|
||||
use crate::pus::tests::CommonTmInfo;
|
||||
use crate::pus::verification::{
|
||||
EcssTmError, EcssTmSender, FailParams, FailParamsWithStep, RequestId, StateNone,
|
||||
EcssTmError, EcssTmSender, FailParams, FailParamsWithStep, RequestId, TcStateNone,
|
||||
VerificationReporter, VerificationReporterCfg, VerificationReporterWithSender,
|
||||
VerificationToken,
|
||||
};
|
||||
@ -1096,8 +1122,9 @@ mod tests {
|
||||
pub service_queue: VecDeque<TmInfo>,
|
||||
}
|
||||
|
||||
impl EcssTmSender<()> for TestSender {
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<()>> {
|
||||
impl EcssTmSender for TestSender {
|
||||
type Error = ();
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>> {
|
||||
assert_eq!(PusPacket::service(&tm), 1);
|
||||
assert!(tm.source_data().is_some());
|
||||
let mut time_stamp = [0; 7];
|
||||
@ -1125,7 +1152,8 @@ mod tests {
|
||||
#[derive(Default)]
|
||||
struct FallibleSender {}
|
||||
|
||||
impl EcssTmSender<DummyError> for FallibleSender {
|
||||
impl EcssTmSender for FallibleSender {
|
||||
type Error = DummyError;
|
||||
fn send_tm(&mut self, _: PusTm) -> Result<(), EcssTmError<DummyError>> {
|
||||
Err(EcssTmError::SendError(DummyError {}))
|
||||
}
|
||||
@ -1167,7 +1195,7 @@ mod tests {
|
||||
(pus_tc, req_id)
|
||||
}
|
||||
|
||||
fn base_init(api_sel: bool) -> (TestBase<'static>, VerificationToken<StateNone>) {
|
||||
fn base_init(api_sel: bool) -> (TestBase<'static>, VerificationToken<TcStateNone>) {
|
||||
let mut reporter = base_reporter();
|
||||
let (tc, req_id) = base_tc_init(None);
|
||||
let init_tok;
|
||||
@ -1181,7 +1209,7 @@ mod tests {
|
||||
|
||||
fn base_with_helper_init() -> (
|
||||
TestBaseWithHelper<'static, ()>,
|
||||
VerificationToken<StateNone>,
|
||||
VerificationToken<TcStateNone>,
|
||||
) {
|
||||
let mut reporter = base_reporter();
|
||||
let (tc, _) = base_tc_init(None);
|
||||
@ -1337,7 +1365,7 @@ mod tests {
|
||||
let fail_code = EcssEnumU8::new(10);
|
||||
let fail_data = EcssEnumU32::new(12);
|
||||
let mut fail_data_raw = [0; 4];
|
||||
fail_data.write_to_bytes(&mut fail_data_raw).unwrap();
|
||||
fail_data.write_to_be_bytes(&mut fail_data_raw).unwrap();
|
||||
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, Some(fail_data_raw.as_slice()));
|
||||
b.vr.acceptance_failure(tok, &mut sender, fail_params)
|
||||
.expect("Sending acceptance success failed");
|
||||
|
94
fsrc-core/tests/pus_autogen_events.rs
Normal file
94
fsrc-core/tests/pus_autogen_events.rs
Normal file
@ -0,0 +1,94 @@
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
use fsrc_core::events::{
|
||||
EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw,
|
||||
Severity, SeverityInfo, SeverityLow, SeverityMedium,
|
||||
};
|
||||
use std::convert::AsRef;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GroupIdIntrospection {
|
||||
name: &'static str,
|
||||
id: LargestGroupIdRaw,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EventIntrospection {
|
||||
name: &'static str,
|
||||
group_id: GroupIdIntrospection,
|
||||
event: &'static EventU32,
|
||||
info: &'static str,
|
||||
}
|
||||
|
||||
//#[event(descr="This is some info event")]
|
||||
const INFO_EVENT_0: EventU32TypedSev<SeverityInfo> = EventU32TypedSev::const_new(0, 0);
|
||||
const INFO_EVENT_0_ERASED: EventU32 = EventU32::const_from_info(INFO_EVENT_0);
|
||||
|
||||
// This is ideally auto-generated
|
||||
const INFO_EVENT_0_INTROSPECTION: EventIntrospection = EventIntrospection {
|
||||
name: "INFO_EVENT_0",
|
||||
group_id: GroupIdIntrospection {
|
||||
id: 0,
|
||||
name: "Group ID 0 without name",
|
||||
},
|
||||
event: &INFO_EVENT_0_ERASED,
|
||||
info: "This is some info event",
|
||||
};
|
||||
|
||||
//#[event(descr="This is some low severity event")]
|
||||
const SOME_LOW_SEV_EVENT: EventU32TypedSev<SeverityLow> = EventU32TypedSev::const_new(0, 12);
|
||||
|
||||
//const EVENT_LIST: [&'static Event; 2] = [&INFO_EVENT_0, &SOME_LOW_SEV_EVENT];
|
||||
|
||||
//#[event_group]
|
||||
const TEST_GROUP_NAME: u16 = 1;
|
||||
// Auto-generated?
|
||||
const TEST_GROUP_NAME_NAME: &str = "TEST_GROUP_NAME";
|
||||
|
||||
//#[event(desc="Some medium severity event")]
|
||||
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP: EventU32TypedSev<SeverityMedium> =
|
||||
EventU32TypedSev::const_new(TEST_GROUP_NAME, 0);
|
||||
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED: EventU32 =
|
||||
EventU32::const_from_medium(MEDIUM_SEV_EVENT_IN_OTHER_GROUP);
|
||||
|
||||
// Also auto-generated
|
||||
const MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION: EventIntrospection = EventIntrospection {
|
||||
name: "MEDIUM_SEV_EVENT_IN_OTHER_GROUP",
|
||||
group_id: GroupIdIntrospection {
|
||||
name: TEST_GROUP_NAME_NAME,
|
||||
id: TEST_GROUP_NAME,
|
||||
},
|
||||
event: &MEDIUM_SEV_EVENT_IN_OTHER_GROUP_REDUCED,
|
||||
info: "Some medium severity event",
|
||||
};
|
||||
|
||||
const CONST_SLICE: &'static [u8] = &[0, 1, 2, 3];
|
||||
const INTROSPECTION_FOR_TEST_GROUP_0: [&EventIntrospection; 2] =
|
||||
[&INFO_EVENT_0_INTROSPECTION, &INFO_EVENT_0_INTROSPECTION];
|
||||
|
||||
//const INTROSPECTION_FOR_TABLE: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_0;
|
||||
|
||||
const INTROSPECTION_FOR_TEST_GROUP_NAME: [&EventIntrospection; 1] =
|
||||
[&MEDIUM_SEV_EVENT_IN_OTHER_GROUP_INTROSPECTION];
|
||||
//const BLAH: &'static [&EventIntrospection] = &INTROSPECTION_FOR_TEST_GROUP_NAME;
|
||||
|
||||
const ALL_EVENTS: [&[&EventIntrospection]; 2] = [
|
||||
&INTROSPECTION_FOR_TEST_GROUP_0,
|
||||
&INTROSPECTION_FOR_TEST_GROUP_NAME,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn main() {
|
||||
//let test = stringify!(INFO_EVENT);
|
||||
//println!("{:?}", test);
|
||||
//for event in EVENT_LIST {
|
||||
// println!("{:?}", event);
|
||||
//}
|
||||
//for events in ALL_EVENTS.into_iter().flatten() {
|
||||
// dbg!("{:?}", events);
|
||||
//}
|
||||
//for introspection_info in INTROSPECTION_FOR_TEST_GROUP {
|
||||
// dbg!("{:?}", introspection_info);
|
||||
//}
|
||||
//let test_struct =
|
||||
}
|
@ -1 +1,168 @@
|
||||
use fsrc_core::event_man::{
|
||||
EventManagerWithMpscQueue, MpscEventU32Receiver, MpscEventU32SendProvider, SendEventProvider,
|
||||
};
|
||||
use fsrc_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
|
||||
use fsrc_core::params::U32Pair;
|
||||
use fsrc_core::params::{Params, ParamsHeapless, WritableToBeBytes};
|
||||
use fsrc_core::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventDispatcher};
|
||||
use fsrc_core::pus::{EcssTmError, EcssTmSender};
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use spacepackets::tm::PusTm;
|
||||
use std::sync::mpsc::{channel, SendError, TryRecvError};
|
||||
use std::thread;
|
||||
|
||||
const INFO_EVENT: EventU32TypedSev<SeverityInfo> =
|
||||
EventU32TypedSev::<SeverityInfo>::const_new(1, 0);
|
||||
const LOW_SEV_EVENT: EventU32 = EventU32::const_new(Severity::LOW, 1, 5);
|
||||
const EMPTY_STAMP: [u8; 7] = [0; 7];
|
||||
|
||||
struct EventTmSender {
|
||||
sender: std::sync::mpsc::Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl EcssTmSender for EventTmSender {
|
||||
type Error = SendError<Vec<u8>>;
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>> {
|
||||
let mut vec = Vec::new();
|
||||
tm.append_to_vec(&mut vec)?;
|
||||
self.sender.send(vec).map_err(EcssTmError::SendError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_threaded_usage() {
|
||||
let (event_sender, event_man_receiver) = channel();
|
||||
let event_receiver = MpscEventU32Receiver::new(event_man_receiver);
|
||||
let mut event_man = EventManagerWithMpscQueue::new(Box::new(event_receiver));
|
||||
|
||||
let (pus_event_man_tx, pus_event_man_rx) = channel();
|
||||
let pus_event_man_send_provider = MpscEventU32SendProvider::new(1, pus_event_man_tx);
|
||||
event_man.subscribe_all(pus_event_man_send_provider.id());
|
||||
event_man.add_sender(pus_event_man_send_provider);
|
||||
let (event_tx, event_rx) = channel();
|
||||
let reporter = EventReporter::new(0x02, 128).expect("Creating event reporter failed");
|
||||
let backend = DefaultPusMgmtBackendProvider::<EventU32>::default();
|
||||
let mut pus_event_man = PusEventDispatcher::new(reporter, Box::new(backend));
|
||||
// PUS + Generic event manager thread
|
||||
let jh0 = thread::spawn(move || {
|
||||
let mut sender = EventTmSender { sender: event_tx };
|
||||
let mut event_cnt = 0;
|
||||
let mut params_array: [u8; 128] = [0; 128];
|
||||
loop {
|
||||
let res = event_man.try_event_handling();
|
||||
assert!(res.is_ok());
|
||||
match pus_event_man_rx.try_recv() {
|
||||
Ok((event, aux_data)) => {
|
||||
let mut gen_event = |aux_data| {
|
||||
pus_event_man.generate_pus_event_tm_generic(
|
||||
&mut sender,
|
||||
&EMPTY_STAMP,
|
||||
event,
|
||||
aux_data,
|
||||
)
|
||||
};
|
||||
let res = if let Some(aux_data) = aux_data {
|
||||
match aux_data {
|
||||
Params::Heapless(heapless) => match heapless {
|
||||
ParamsHeapless::Raw(raw) => {
|
||||
raw.write_to_be_bytes(&mut params_array)
|
||||
.expect("Writing raw parameter failed");
|
||||
gen_event(Some(¶ms_array[0..raw.raw_len()]))
|
||||
}
|
||||
ParamsHeapless::EcssEnum(e) => {
|
||||
e.write_to_be_bytes(&mut params_array)
|
||||
.expect("Writing ECSS enum failed");
|
||||
gen_event(Some(¶ms_array[0..e.raw_len()]))
|
||||
}
|
||||
ParamsHeapless::Store(_) => gen_event(None),
|
||||
},
|
||||
Params::Vec(vec) => gen_event(Some(vec.as_slice())),
|
||||
Params::String(str) => gen_event(Some(str.as_bytes())),
|
||||
}
|
||||
} else {
|
||||
gen_event(None)
|
||||
};
|
||||
event_cnt += 1;
|
||||
assert!(res.is_ok());
|
||||
assert!(res.unwrap());
|
||||
if event_cnt == 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if let TryRecvError::Disconnected = e {
|
||||
panic!("Event receiver disconnected!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Event sender and TM checker thread
|
||||
let jh1 = thread::spawn(move || {
|
||||
event_sender
|
||||
.send((INFO_EVENT.into(), None))
|
||||
.expect("Sending info event failed");
|
||||
loop {
|
||||
match event_rx.try_recv() {
|
||||
// Event TM received successfully
|
||||
Ok(event_tm) => {
|
||||
let tm =
|
||||
PusTm::from_bytes(event_tm.as_slice(), 7).expect("Deserializing TM failed");
|
||||
assert_eq!(tm.0.service(), 5);
|
||||
assert_eq!(tm.0.subservice(), 1);
|
||||
let src_data = tm.0.source_data();
|
||||
assert!(src_data.is_some());
|
||||
let src_data = src_data.unwrap();
|
||||
assert_eq!(src_data.len(), 4);
|
||||
let event =
|
||||
EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap()));
|
||||
assert_eq!(event, INFO_EVENT);
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
if let TryRecvError::Disconnected = e {
|
||||
panic!("Event sender disconnected!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
event_sender
|
||||
.send((
|
||||
LOW_SEV_EVENT.into(),
|
||||
Some(Params::Heapless((2_u32, 3_u32).into())),
|
||||
))
|
||||
.expect("Sending low severity event failed");
|
||||
loop {
|
||||
match event_rx.try_recv() {
|
||||
// Event TM received successfully
|
||||
Ok(event_tm) => {
|
||||
let tm =
|
||||
PusTm::from_bytes(event_tm.as_slice(), 7).expect("Deserializing TM failed");
|
||||
assert_eq!(tm.0.service(), 5);
|
||||
assert_eq!(tm.0.subservice(), 2);
|
||||
let src_data = tm.0.source_data();
|
||||
assert!(src_data.is_some());
|
||||
let src_data = src_data.unwrap();
|
||||
assert_eq!(src_data.len(), 12);
|
||||
let event =
|
||||
EventU32::from(u32::from_be_bytes(src_data[0..4].try_into().unwrap()));
|
||||
assert_eq!(event, LOW_SEV_EVENT);
|
||||
let u32_pair: U32Pair =
|
||||
src_data[4..].try_into().expect("Creating U32Pair failed");
|
||||
assert_eq!(u32_pair.0, 2);
|
||||
assert_eq!(u32_pair.1, 3);
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
if let TryRecvError::Disconnected = e {
|
||||
panic!("Event sender disconnected!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
jh0.join().expect("Joining manager thread failed");
|
||||
jh1.join().expect("Joining creator thread failed");
|
||||
}
|
||||
|
@ -2,15 +2,27 @@ mod ccsds;
|
||||
mod pus;
|
||||
mod tmtc;
|
||||
|
||||
use crate::tmtc::{core_tmtc_task, TmStore, PUS_APID};
|
||||
use crate::tmtc::{core_tmtc_task, CoreTmtcArgs, TmStore, PUS_APID};
|
||||
use fsrc_core::event_man::{
|
||||
EventManagerWithMpscQueue, MpscEventReceiver, MpscEventU32SendProvider, SendEventProvider,
|
||||
};
|
||||
use fsrc_core::events::EventU32;
|
||||
use fsrc_core::hal::host::udp_server::UdpTcServer;
|
||||
use fsrc_core::pool::{LocalPool, PoolCfg, SharedPool, StoreAddr};
|
||||
use fsrc_core::pus::event_man::{
|
||||
DefaultPusMgmtBackendProvider, EventReporter, EventRequest, EventRequestWithToken,
|
||||
PusEventDispatcher,
|
||||
};
|
||||
use fsrc_core::pus::verification::{
|
||||
MpscVerifSender, VerificationReporterCfg, VerificationReporterWithSender,
|
||||
};
|
||||
use fsrc_core::pus::{EcssTmError, EcssTmSender};
|
||||
use fsrc_core::tmtc::CcsdsError;
|
||||
use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||
use spacepackets::time::{CdsShortTimeProvider, TimeWriter};
|
||||
use spacepackets::tm::PusTm;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{mpsc, Arc, Mutex, RwLock};
|
||||
use std::thread;
|
||||
|
||||
@ -27,6 +39,28 @@ struct UdpTmtcServer {
|
||||
|
||||
unsafe impl Send for UdpTmtcServer {}
|
||||
|
||||
struct EventTmSender {
|
||||
store_helper: TmStore,
|
||||
sender: mpsc::Sender<StoreAddr>,
|
||||
}
|
||||
|
||||
impl EventTmSender {
|
||||
fn new(store_helper: TmStore, sender: mpsc::Sender<StoreAddr>) -> Self {
|
||||
Self {
|
||||
store_helper,
|
||||
sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EcssTmSender for EventTmSender {
|
||||
type Error = mpsc::SendError<StoreAddr>;
|
||||
|
||||
fn send_tm(&mut self, tm: PusTm) -> Result<(), EcssTmError<Self::Error>> {
|
||||
let addr = self.store_helper.add_pus_tm(&tm);
|
||||
self.sender.send(addr).map_err(EcssTmError::SendError)
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
println!("Running OBSW example");
|
||||
let pool_cfg = PoolCfg::new(vec![(8, 32), (4, 64), (2, 128)]);
|
||||
@ -36,22 +70,38 @@ fn main() {
|
||||
pool: tm_store.clone(),
|
||||
};
|
||||
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||
let (tm_funnel_tx, tm_funnel_rx) = mpsc::channel();
|
||||
let (tm_server_tx, tm_server_rx) = mpsc::channel();
|
||||
let (tm_funnel_tx, tm_funnel_rx) = channel();
|
||||
let (tm_server_tx, tm_server_rx) = channel();
|
||||
let sender = MpscVerifSender::new(tm_store.clone(), tm_funnel_tx.clone());
|
||||
let verif_cfg = VerificationReporterCfg::new(PUS_APID, 1, 2, 8).unwrap();
|
||||
let reporter_with_sender_0 = Arc::new(Mutex::new(VerificationReporterWithSender::new(
|
||||
verif_cfg,
|
||||
Box::new(sender),
|
||||
)));
|
||||
|
||||
// Create event handling components
|
||||
let (event_request_tx, event_request_rx) = channel::<EventRequestWithToken>();
|
||||
let (event_sender, event_man_rx) = channel();
|
||||
let event_recv = MpscEventReceiver::<EventU32>::new(event_man_rx);
|
||||
let mut event_man = EventManagerWithMpscQueue::new(Box::new(event_recv));
|
||||
let event_reporter = EventReporter::new(PUS_APID, 128).unwrap();
|
||||
let pus_tm_backend = DefaultPusMgmtBackendProvider::<EventU32>::default();
|
||||
let mut pus_event_dispatcher =
|
||||
PusEventDispatcher::new(event_reporter, Box::new(pus_tm_backend));
|
||||
let (pus_event_man_tx, pus_event_man_rx) = channel();
|
||||
let pus_event_man_send_provider = MpscEventU32SendProvider::new(1, pus_event_man_tx);
|
||||
let reporter1 = reporter_with_sender_0.clone();
|
||||
event_man.subscribe_all(pus_event_man_send_provider.id());
|
||||
|
||||
// Create clones here to allow move for thread 0
|
||||
let core_args = CoreTmtcArgs {
|
||||
tm_store: tm_store_helper.clone(),
|
||||
tm_sender: tm_funnel_tx.clone(),
|
||||
event_sender,
|
||||
event_request_tx,
|
||||
};
|
||||
let jh0 = thread::spawn(move || {
|
||||
core_tmtc_task(
|
||||
tm_funnel_tx.clone(),
|
||||
tm_server_rx,
|
||||
tm_store_helper.clone(),
|
||||
addr,
|
||||
reporter_with_sender_0,
|
||||
);
|
||||
core_tmtc_task(core_args, tm_server_rx, addr, reporter_with_sender_0);
|
||||
});
|
||||
|
||||
let jh1 = thread::spawn(move || {
|
||||
@ -68,6 +118,57 @@ fn main() {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let jh2 = thread::spawn(move || {
|
||||
let mut timestamp: [u8; 7] = [0; 7];
|
||||
let mut sender = EventTmSender::new(tm_store_helper, tm_funnel_tx);
|
||||
let mut time_provider = CdsShortTimeProvider::new(0, 0);
|
||||
let report_completion = |event_req: EventRequestWithToken, timestamp: &[u8]| {
|
||||
let mut reporter = reporter1
|
||||
.lock()
|
||||
.expect("Locking Verification reporter failed");
|
||||
reporter
|
||||
.completion_success(event_req.token, timestamp)
|
||||
.expect("Sending completion success failed");
|
||||
};
|
||||
loop {
|
||||
if let Ok(event_req) = event_request_rx.try_recv() {
|
||||
match event_req.request {
|
||||
EventRequest::Enable(event) => {
|
||||
pus_event_dispatcher
|
||||
.enable_tm_for_event(&event)
|
||||
.expect("Enabling TM failed");
|
||||
update_time(&mut time_provider, &mut timestamp);
|
||||
report_completion(event_req, ×tamp);
|
||||
}
|
||||
EventRequest::Disable(event) => {
|
||||
pus_event_dispatcher
|
||||
.disable_tm_for_event(&event)
|
||||
.expect("Disabling TM failed");
|
||||
update_time(&mut time_provider, &mut timestamp);
|
||||
report_completion(event_req, ×tamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok((event, _param)) = pus_event_man_rx.try_recv() {
|
||||
update_time(&mut time_provider, &mut timestamp);
|
||||
pus_event_dispatcher
|
||||
.generate_pus_event_tm_generic(&mut sender, ×tamp, event, None)
|
||||
.expect("Sending TM as event failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
jh0.join().expect("Joining UDP TMTC server thread failed");
|
||||
jh1.join().expect("Joining TM Funnel thread failed");
|
||||
jh2.join().expect("Joining Event Manager thread failed");
|
||||
}
|
||||
|
||||
pub fn update_time(time_provider: &mut CdsShortTimeProvider, timestamp: &mut [u8]) {
|
||||
time_provider
|
||||
.update_from_now()
|
||||
.expect("Could not get current time");
|
||||
time_provider
|
||||
.write_to_bytes(timestamp)
|
||||
.expect("Writing timestamp failed");
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
use crate::tmtc::TmStore;
|
||||
use fsrc_core::events::EventU32;
|
||||
use fsrc_core::pool::StoreAddr;
|
||||
use fsrc_core::pus::event::Subservices;
|
||||
use fsrc_core::pus::event_man::{EventRequest, EventRequestWithToken};
|
||||
use fsrc_core::pus::verification::{
|
||||
SharedStdVerifReporterWithSender, StateAccepted, VerificationToken,
|
||||
FailParams, SharedStdVerifReporterWithSender, TcStateAccepted, VerificationToken,
|
||||
};
|
||||
use fsrc_core::tmtc::tm_helper::PusTmWithCdsShortHelper;
|
||||
use fsrc_core::tmtc::PusServiceProvider;
|
||||
use spacepackets::tc::{PusTc, PusTcSecondaryHeaderT};
|
||||
use spacepackets::ecss::{EcssEnumU16, PusPacket};
|
||||
use spacepackets::tc::PusTc;
|
||||
use spacepackets::time::{CdsShortTimeProvider, TimeWriter};
|
||||
use spacepackets::SpHeader;
|
||||
use std::sync::mpsc;
|
||||
@ -15,6 +19,7 @@ pub struct PusReceiver {
|
||||
pub tm_tx: mpsc::Sender<StoreAddr>,
|
||||
pub tm_store: TmStore,
|
||||
pub verif_reporter: SharedStdVerifReporterWithSender,
|
||||
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
||||
stamper: CdsShortTimeProvider,
|
||||
time_stamp: [u8; 7],
|
||||
}
|
||||
@ -25,12 +30,14 @@ impl PusReceiver {
|
||||
tm_tx: mpsc::Sender<StoreAddr>,
|
||||
tm_store: TmStore,
|
||||
verif_reporter: SharedStdVerifReporterWithSender,
|
||||
event_request_tx: mpsc::Sender<EventRequestWithToken>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tm_helper: PusTmWithCdsShortHelper::new(apid),
|
||||
tm_tx,
|
||||
tm_store,
|
||||
verif_reporter,
|
||||
event_request_tx,
|
||||
stamper: CdsShortTimeProvider::default(),
|
||||
time_stamp: [0; 7],
|
||||
}
|
||||
@ -63,13 +70,15 @@ impl PusServiceProvider for PusReceiver {
|
||||
drop(reporter);
|
||||
if service == 17 {
|
||||
self.handle_test_service(pus_tc, accepted_token);
|
||||
} else if service == 5 {
|
||||
self.handle_event_service(pus_tc, accepted_token);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PusReceiver {
|
||||
fn handle_test_service(&mut self, pus_tc: &PusTc, token: VerificationToken<StateAccepted>) {
|
||||
fn handle_test_service(&mut self, pus_tc: &PusTc, token: VerificationToken<TcStateAccepted>) {
|
||||
if pus_tc.subservice() == 1 {
|
||||
println!("Received PUS ping command TC[17,1]");
|
||||
println!("Sending ping reply PUS TM[17,2]");
|
||||
@ -90,4 +99,90 @@ impl PusReceiver {
|
||||
.expect("Error sending completion success");
|
||||
}
|
||||
}
|
||||
|
||||
fn update_time_stamp(&mut self) {
|
||||
self.stamper
|
||||
.update_from_now()
|
||||
.expect("Updating timestamp failed");
|
||||
self.stamper
|
||||
.write_to_bytes(&mut self.time_stamp)
|
||||
.expect("Writing timestamp failed");
|
||||
}
|
||||
|
||||
fn handle_event_service(&mut self, pus_tc: &PusTc, token: VerificationToken<TcStateAccepted>) {
|
||||
let send_start_failure = |verif_reporter: &mut SharedStdVerifReporterWithSender,
|
||||
timestamp: &[u8; 7],
|
||||
failure_code: EcssEnumU16,
|
||||
failure_data: Option<&[u8]>| {
|
||||
verif_reporter
|
||||
.lock()
|
||||
.expect("Locking verification reporter failed")
|
||||
.start_failure(
|
||||
token,
|
||||
FailParams::new(timestamp, &failure_code, failure_data),
|
||||
)
|
||||
.expect("Sending start failure TM failed");
|
||||
};
|
||||
let send_start_acceptance = |verif_reporter: &mut SharedStdVerifReporterWithSender,
|
||||
timestamp: &[u8; 7]| {
|
||||
verif_reporter
|
||||
.lock()
|
||||
.expect("Locking verification reporter failed")
|
||||
.start_success(token, timestamp)
|
||||
.expect("Sending start success TM failed")
|
||||
};
|
||||
if pus_tc.user_data().is_none() {
|
||||
self.update_time_stamp();
|
||||
send_start_failure(
|
||||
&mut self.verif_reporter,
|
||||
&self.time_stamp,
|
||||
EcssEnumU16::new(1),
|
||||
None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let app_data = pus_tc.user_data().unwrap();
|
||||
if app_data.len() < 4 {
|
||||
self.update_time_stamp();
|
||||
send_start_failure(
|
||||
&mut self.verif_reporter,
|
||||
&self.time_stamp,
|
||||
EcssEnumU16::new(1),
|
||||
None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let event_id = EventU32::from(u32::from_be_bytes(app_data.try_into().unwrap()));
|
||||
match PusPacket::subservice(pus_tc).try_into() {
|
||||
Ok(Subservices::TcEnableEventGeneration) => {
|
||||
self.update_time_stamp();
|
||||
let start_token = send_start_acceptance(&mut self.verif_reporter, &self.time_stamp);
|
||||
self.event_request_tx
|
||||
.send(EventRequestWithToken {
|
||||
request: EventRequest::Enable(event_id),
|
||||
token: start_token,
|
||||
})
|
||||
.expect("Sending event request failed");
|
||||
}
|
||||
Ok(Subservices::TcDisableEventGeneration) => {
|
||||
self.update_time_stamp();
|
||||
let start_token = send_start_acceptance(&mut self.verif_reporter, &self.time_stamp);
|
||||
self.event_request_tx
|
||||
.send(EventRequestWithToken {
|
||||
request: EventRequest::Disable(event_id),
|
||||
token: start_token,
|
||||
})
|
||||
.expect("Sending event request failed");
|
||||
}
|
||||
_ => {
|
||||
self.update_time_stamp();
|
||||
send_start_failure(
|
||||
&mut self.verif_reporter,
|
||||
&self.time_stamp,
|
||||
EcssEnumU16::new(2),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
use fsrc_core::events::EventU32;
|
||||
use fsrc_core::hal::host::udp_server::{ReceiveResult, UdpTcServer};
|
||||
use fsrc_core::params::Params;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
@ -8,12 +11,20 @@ use crate::ccsds::CcsdsReceiver;
|
||||
use crate::pus::PusReceiver;
|
||||
use crate::UdpTmtcServer;
|
||||
use fsrc_core::pool::{SharedPool, StoreAddr};
|
||||
use fsrc_core::pus::event_man::EventRequestWithToken;
|
||||
use fsrc_core::pus::verification::SharedStdVerifReporterWithSender;
|
||||
use fsrc_core::tmtc::{CcsdsDistributor, CcsdsError, PusDistributor};
|
||||
use spacepackets::tm::PusTm;
|
||||
|
||||
pub const PUS_APID: u16 = 0x02;
|
||||
|
||||
pub struct CoreTmtcArgs {
|
||||
pub tm_store: TmStore,
|
||||
pub tm_sender: Sender<StoreAddr>,
|
||||
pub event_sender: Sender<(EventU32, Option<Params>)>,
|
||||
pub event_request_tx: Sender<EventRequestWithToken>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TmStore {
|
||||
pub pool: SharedPool,
|
||||
@ -31,17 +42,17 @@ impl TmStore {
|
||||
}
|
||||
|
||||
pub fn core_tmtc_task(
|
||||
tm_creator_tx: mpsc::Sender<StoreAddr>,
|
||||
args: CoreTmtcArgs,
|
||||
tm_server_rx: mpsc::Receiver<StoreAddr>,
|
||||
tm_store_helper: TmStore,
|
||||
addr: SocketAddr,
|
||||
verif_reporter: SharedStdVerifReporterWithSender,
|
||||
) {
|
||||
let pus_receiver = PusReceiver::new(
|
||||
PUS_APID,
|
||||
tm_creator_tx,
|
||||
tm_store_helper.clone(),
|
||||
args.tm_sender,
|
||||
args.tm_store.clone(),
|
||||
verif_reporter,
|
||||
args.event_request_tx,
|
||||
);
|
||||
let pus_distributor = PusDistributor::new(Box::new(pus_receiver));
|
||||
let ccsds_receiver = CcsdsReceiver {
|
||||
@ -54,7 +65,7 @@ pub fn core_tmtc_task(
|
||||
let mut udp_tmtc_server = UdpTmtcServer {
|
||||
udp_tc_server,
|
||||
tm_rx: tm_server_rx,
|
||||
tm_store: tm_store_helper.pool.clone(),
|
||||
tm_store: args.tm_store.pool.clone(),
|
||||
};
|
||||
loop {
|
||||
core_tmtc_loop(&mut udp_tmtc_server);
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit a2673c98707ecbbabb9535bef607025c92b54724
|
||||
Subproject commit c750f94fba31114c5ade530ea6235acb6bb8835a
|
Loading…
Reference in New Issue
Block a user