4 Commits

Author SHA1 Message Date
30e2a354cb doc fixes
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
2024-02-10 11:53:46 +01:00
0af6b5283d documentation fixes
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good
2024-02-09 19:26:55 +01:00
3e5f05e634 fixed all tests
All checks were successful
Rust/sat-rs/pipeline/head This commit looks good
2024-02-09 19:21:49 +01:00
b5efc0081c start refactoring the pool
Some checks failed
Rust/sat-rs/pipeline/head There was a failure building this commit
2024-02-09 16:48:02 +01:00
126 changed files with 1891 additions and 7484 deletions

View File

@ -1,10 +1,9 @@
[workspace]
resolver = "2"
members = [
"satrs",
"satrs-core",
"satrs-mib",
"satrs-example",
"satrs-shared",
]
exclude = [

View File

@ -1,10 +1,5 @@
<p align="center"> <img src="misc/satrs-logo.png" width="40%"> </p>
[![sat-rs website](https://img.shields.io/badge/sat--rs-website-darkgreen?style=flat)](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/)
[![sat-rs book](https://img.shields.io/badge/sat--rs-book-darkgreen?style=flat)](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/)
[![Crates.io](https://img.shields.io/crates/v/satrs)](https://crates.io/crates/satrs)
[![docs.rs](https://img.shields.io/docsrs/satrs)](https://docs.rs/satrs)
sat-rs
=========
@ -12,7 +7,7 @@ This is the repository of the sat-rs framework. Its primary goal is to provide r
to write on-board software for remote systems like rovers or satellites. It is specifically written
for the special requirements for these systems. You can find an overview of the project and the
link to the [more high-level sat-rs book](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/)
at the [IRS software projects website](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/).
at the [IRS documentation website](https://absatsw.irs.uni-stuttgart.de/sat-rs.html).
A lot of the architecture and general design considerations are based on the
[FSFW](https://egit.irs.uni-stuttgart.de/fsfw/fsfw) C++ framework which has flight heritage
@ -27,12 +22,12 @@ This project currently contains following crates:
Primary information resource in addition to the API documentation, hosted
[here](https://documentation.irs.uni-stuttgart.de/projects/sat-rs/). It can be useful to read
this first before delving into the example application and the API documentation.
* [`satrs`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs):
Primary crate.
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example):
* [`satrs-core`](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad/src/branch/main/satrs-core):
Core components of sat-rs.
* [`satrs-example`](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad/src/branch/main/satrs-example):
Example of a simple example on-board software using various sat-rs components which can be run
on a host computer or on any system with a standard runtime like a Raspberry Pi.
* [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-mib):
* [`satrs-mib`](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad/src/branch/main/satrs-mib):
Components to build a mission information base from the on-board software directly.
* [`satrs-example-stm32f3-disco`](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example-stm32f3-disco):
Example of a simple example on-board software using sat-rs components on a bare-metal system
@ -46,7 +41,7 @@ Each project has its own `CHANGELOG.md`.
* [`spacepackets`](https://egit.irs.uni-stuttgart.de/rust/spacepackets): Basic ECSS and CCSDS
packet protocol implementations. This repository is re-exported in the
[`satrs`](https://egit.irs.uni-stuttgart.de/rust/satrs/src/branch/main/satrs)
[`satrs-core`](https://egit.irs.uni-stuttgart.de/rust/satrs-launchpad/src/branch/main/satrs-core)
crate.
# Coverage

View File

@ -15,9 +15,7 @@ RUN rustup install nightly && \
rustup component add rustfmt clippy
WORKDIR "/tmp"
# Install cargo-nextest
RUN curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
# Install mdbook and mdbook-linkcheck
# RUN cargo install mdbook --no-default-features --features search --vers "^0.4" --locked
RUN curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.37/mdbook-v0.4.37-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory /usr/local/bin
RUN curl -sSL https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip -o mdbook-linkcheck.zip && \
unzip mdbook-linkcheck.zip && \

View File

@ -32,7 +32,7 @@ pipeline {
}
stage('Test') {
steps {
sh 'cargo nextest r --all-features'
sh 'cargo test --all-features'
}
}
stage('Check with all features') {
@ -47,7 +47,7 @@ pipeline {
}
stage('Check Cross Embedded Bare Metal') {
steps {
sh 'cargo check -p satrs --target thumbv7em-none-eabihf --no-default-features'
sh 'cargo check -p satrs-core --target thumbv7em-none-eabihf --no-default-features'
}
}
stage('Check Cross Embedded Linux') {

View File

@ -43,8 +43,8 @@ def main():
parser.add_argument(
"-p",
"--package",
choices=["satrs"],
default="satrs",
choices=["satrs-core"],
default="satrs-core",
help="Choose project to generate coverage for",
)
parser.add_argument(

View File

@ -1,650 +0,0 @@
<?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.23.2-->
<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"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="157.79999999999998" width="452.0" x="959.3461887999997" y="585.7236400000005"/>
<y:Fill color2="#000000" hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="145.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="434.0" x="9.0" xml:space="preserve" y="6.399999999999977">&lt;html&gt;
&lt;center&gt;
&lt;h4&gt;ACS Mode Tree&lt;/h4&gt;
&lt;/center&gt;
&lt;table border="1"&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;MGMs&lt;/th&gt;
&lt;th&gt;SUSs&lt;/th&gt;
&lt;th&gt;STR&lt;/th&gt;
&lt;th&gt;MGT&lt;/th&gt;
&lt;th&gt;RWs&lt;/th&gt;
&lt;th&gt;ACS CTRL&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SAFE&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;OFF&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;SAFE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IDLE&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;IDLE&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/html&gt;<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="-1.1102230246251565E-16" 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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="134.60000000000014" width="452.0" x="959.3461887999997" y="757.2703199999999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="120.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="428.0" x="12.0" xml:space="preserve" y="7.300000000000068">&lt;html&gt;
&lt;center&gt;
&lt;h4&gt;ACS IDLE Sequence&lt;/h4&gt;
&lt;/center&gt;
&lt;table border="1"&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;MGMs&lt;/th&gt;
&lt;th&gt;SUS&lt;/th&gt;
&lt;th&gt;STR&lt;/th&gt;
&lt;th&gt;MGT&lt;/th&gt;
&lt;th&gt;RWs&lt;/th&gt;
&lt;th&gt;ACS CTRL&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;NORMAL&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;SAFE&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/html&gt;<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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="32.400000000000034" width="146.79999999999995" x="1128.4" y="313.94999999999993"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="113.94921875" x="16.42539062500009" xml:space="preserve" y="6.0515625000000455">ACS Subsystem<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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="80.39999999999998" x="910.7200000000004" y="407.20000000000005"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="61.837890625" x="9.281054687499932" xml:space="preserve" y="4.03125">MGM
Assembly<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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="32.400000000000034" width="146.79999999999995" x="1133.6000000000001" y="243.3579999999999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.99609375" x="14.401953124999864" xml:space="preserve" y="6.051562499999989">Satellite System<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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="72.40000000000009" x="1021.1200000000003" y="404.99560000000014"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="61.837890625" x="5.2810546875000455" xml:space="preserve" y="4.03125">SUS
Assembly<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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="67.59999999999991" x="1124.92" y="404.99560000000014"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.599609375" x="5.5001953124999545" xml:space="preserve" y="4.03125">STR
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="n7">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="67.59999999999991" x="1208.74" y="407.20000000000005"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="56.599609375" x="5.5001953124999545" xml:space="preserve" y="4.03125">MGT
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="n8">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="72.40000000000009" x="1292.56" y="407.20000000000005"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="61.837890625" x="5.2810546875000455" xml:space="preserve" y="4.03125">RW
Assembly<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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="67.59999999999991" x="1381.18" y="407.20000000000005"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="34.732421875" x="16.433789062499955" xml:space="preserve" y="4.03125">ACS
CTRL<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="n10">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="946.4000000000004" y="463.3000000000002"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="41.640625" x="1.5396874999999" xml:space="preserve" y="1.0156250000000568">MGM0<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="n11">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="946.4000000000004" y="491.32480000000066"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="41.640625" x="1.5396874999999" xml:space="preserve" y="1.015625">MGM1<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="n12">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="946.4000000000004" y="519.3496000000011"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="41.640625" x="1.5396874999999" xml:space="preserve" y="1.015625">MGM2<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="n13">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1055.2600000000007" y="463.3000000000002"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="35.65234375" x="4.5338281249999" xml:space="preserve" y="1.015625">SUS0<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="n14">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1055.2600000000007" y="491.32480000000066"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="15.443359375" x="14.6383203124999" xml:space="preserve" y="1.015625">...<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="n15">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1055.2600000000007" y="519.3496000000011"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="43.287109375" x="0.7164453124999" xml:space="preserve" y="1.015625">SUS12<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="n16">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="50.0" x="1129.9800000000005" y="482.79120000000023"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="45.0390625" x="2.48046875" xml:space="preserve" y="4.03125">STR
Device<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="n17">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="50.0" x="1216.4400000000005" y="482.7912000000002"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="45.0390625" x="2.48046875" xml:space="preserve" y="4.031249999999943">MGT
Device<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="n18">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="454.7884800000007"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW0<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="n19">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="482.3769600000013"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW1<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="n20">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="512.3769600000013"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW2<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="n21">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="20.0" width="44.719999999999914" x="1341.6661887999999" y="542.3769600000013"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="31.837890625" x="6.4410546874999" xml:space="preserve" y="1.015625">RW3<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="n22">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="32.400000000000034" width="59.75999999999999" x="1305.2" y="313.94999999999993"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="17.3505859375" x="21.20470703125011" xml:space="preserve" y="6.051562499999989">...<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>
<edge id="e0" source="n4" target="n2">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-5.199999999999818" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e1" source="n2" target="n3">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1200.63" y="379.7"/>
<y:Point x="950.9200000000003" y="379.7"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e2" source="n2" target="n5">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1200.63" y="379.7"/>
<y:Point x="1057.3200000000004" y="379.7"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e3" source="n2" target="n6">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1200.63" y="379.7"/>
<y:Point x="1158.72" y="379.7"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e4" source="n2" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.1700000000000728" sy="0.0" tx="-11.29999999999859" ty="0.0">
<y:Point x="1200.63" y="379.7"/>
<y:Point x="1231.2400000000014" y="379.7"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e5" source="n2" target="n8">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1200.63" y="379.7"/>
<y:Point x="1328.76" y="379.7"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e6" source="n2" target="n9">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-1.1700000000000728" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="1200.63" y="379.7"/>
<y:Point x="1414.98" y="379.7"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e7" source="n3" target="n10">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-28.009798234586242" sy="0.0" tx="13.11999999999989" ty="0.0">
<y:Point x="922.9102017654141" y="473.3000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e8" source="n3" target="n11">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-28.009798234586242" sy="17.706497359551577" tx="0.0" ty="0.0">
<y:Point x="922.9102017654141" y="501.32480000000066"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e9" source="n3" target="n12">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-28.009798234586242" sy="12.053248679775777" tx="0.0" ty="0.0">
<y:Point x="922.9102017654141" y="529.3496000000011"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e10" source="n5" target="n13">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-24.18711646917177" sy="11.599696230174459" tx="-20.855199999999513" ty="-1.9751999999995178">
<y:Point x="1033.1328835308286" y="471.32480000000066"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e11" source="n5" target="n14">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-24.18711646917177" sy="0.0" tx="-20.855199999999513" ty="-3.024800000000596">
<y:Point x="1033.1328835308286" y="498.30000000000007"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n6" target="n16">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="3.7399999999995543" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n7" target="n17">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="1.0999999999994543" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e14" source="n8" target="n18">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-23.248152938343992" sy="14.026599270174415" tx="-13.026188800000227" ty="0.33167708069714763">
<y:Point x="1305.511847061656" y="465.1201570806978"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e15" source="n8" target="n19">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-23.248152938343992" sy="0.0" tx="-13.026188800000227" ty="0.0">
<y:Point x="1305.511847061656" y="492.3769600000013"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e16" source="n8" target="n20">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-23.248152938343992" sy="0.0" tx="-13.026188800000227" ty="0.0">
<y:Point x="1305.511847061656" y="522.3769600000013"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e17" source="n8" target="n21">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-23.248152938343992" sy="0.0" tx="-13.026188800000227" ty="-1.1368683772161603E-13">
<y:Point x="1305.511847061656" y="552.3769600000012"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e18" source="n4" target="n22">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-5.199999999999818" sy="16.200000000000045" tx="0.0" ty="0.0">
<y:Point x="1201.8000000000002" y="290.43031999999994"/>
<y:Point x="1335.08" y="290.43031999999994"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e19" source="n5" target="n15">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="-24.18711646917177" sy="0.0" tx="-20.855199999999513" ty="0.0">
<y:Point x="1033.1328835308286" y="529.3496000000011"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,285 +0,0 @@
<?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.23.2-->
<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="261.7582999999998" width="631.1152000000001" x="810.8848" y="142.00000000000003"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="296.09375" x="26.51035059931519" xml:space="preserve" y="9.42072533356739">satrs-example Component Structure<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.4579944349315068" nodeRatioY="-0.46400983146067415" 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" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="217.68500000000003" width="166.9147999999998" x="819.9999999999999" y="180.434265625"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="5.0" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="166.9147999999998" x="13.181757989188668" xml:space="preserve" y="6.728692946816665">Application
Components<y:LabelModel><y:SmartNodeLabelModel distance="5.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.4210270270270303" labelRatioY="-0.5" nodeRatioX="0.5" nodeRatioY="-0.46908977216245173" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="39" topF="38.90500000000003"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 3</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n1:">
<node id="n1::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="136.9147999999998" x="834.9999999999999" y="234.33926562500002"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="113.94921875" x="11.482790624999893" xml:space="preserve" y="4.851562500000028">ACS Subsystem<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="n1::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="136.9147999999998" x="834.9999999999999" y="270.5687968750001"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="111.884765625" x="12.515017187499893" xml:space="preserve" y="4.8515625">EPS Subsystem<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="n1::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="136.9147999999998" x="834.9999999999999" y="306.84403125000006"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="112.923828125" x="11.995485937499893" xml:space="preserve" y="4.8515625">TCS Subsystem<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="n1::n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="136.9147999999998" x="834.9999999999999" y="343.119265625"/>
<y:Fill color="#FFCC00" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.59375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.259765625" x="27.827517187499893" xml:space="preserve" y="1.703125">Payload
Subsystem<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>
</graph>
</node>
<node id="n2" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="261.7582999999998" width="498.25116669136776" x="971.9147999999997" y="151.36096562500023"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="false" width="4.0" x="247.12558334568382" y="0.0"/>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="7" leftF="7.00529999999992" right="3" rightF="3.313607121059931" top="14" topF="14.073299999999762"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 4</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n2:">
<node id="n2::n0" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="217.68500000000003" width="441.0158999999999" x="993.9200999999996" y="180.434265625"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" type="line" width="1.0"/>
<y:NodeLabel alignment="left" autoSizePolicy="node_width" borderDistance="5.0" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="441.0158999999999" x="16.916359570308032" xml:space="preserve" y="5.969557999968089">Generic Components<y:LabelModel><y:SmartNodeLabelModel distance="5.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.46164229096885634" labelRatioY="-0.5" nodeRatioX="0.5" nodeRatioY="-0.4725770815629552" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="22" bottomF="22.006468749999954" left="0" leftF="0.0" right="0" rightF="0.0" top="28" topF="27.68500000000006"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 2</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n2::n0:">
<node id="n2::n0::n0">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="125.0" x="1151.9280499999995" y="281.84403125000006"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.255859375" x="24.3720703125" xml:space="preserve" y="4.8515625">TM Funnel<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::n0::n1">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="125.0" x="1151.9280499999995" y="330.5687968750001"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="105.7119140625" x="9.64404296875" xml:space="preserve" y="4.8515625">TCP/IP Servers<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::n0::n2">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="125.0" x="1294.9359999999995" y="281.84403125000006"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="75.1689453125" x="24.91552734375" xml:space="preserve" y="4.8515625">TC Source<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::n0::n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="125.0" x="1008.9200999999996" y="223.11926562500005"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="109.9228515625" x="7.53857421875" xml:space="preserve" y="4.8515625">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::n0::n4">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="125.0" x="1008.9200999999996" y="267.1160312500001"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.7021484375" x="3.64892578125" xml:space="preserve" y="4.8515625">PUS Distribution<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::n0::n5">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="40.0" width="125.0" x="1151.9280499999995" y="223.11926562500005"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="dashed" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.59375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="84.1650390625" x="20.41748046875" xml:space="preserve" y="1.7031249999999716">Shared
TMTC Pools<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::n0::n6">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="50.0" width="125.0" x="1008.9200999999996" y="311.1127968750001"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="36.59375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="78.12890625" x="23.435546875" xml:space="preserve" y="6.703125">Satellite
Mode Tree<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::n0::n7">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="30.0" width="125.0" x="1294.9359999999995" y="223.11926562500005"/>
<y:Fill color="#CCFFFF" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="14" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="20.296875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="74.7861328125" x="25.10693359375" xml:space="preserve" y="4.8515625"> PUS Stack<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>
</graph>
</node>
</graph>
</node>
<node id="n3">
<data key="d6">
<y:ShapeNode>
<y:Geometry height="57.265600000000006" width="631.1152" x="810.8847999999999" y="411.39428125"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="261.8125" x="166.89412267941418" xml:space="preserve" y="3.144146301369915">satrs-satellite
Simulator based on asynchronix<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" 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="50.0" width="631.1152000000002" x="810.8847999999998" y="476.2958625000002"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="41.25" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="374.8359375" x="110.3824039294143" xml:space="preserve" y="0.12842465753431043">satrs-tmtc
Command-line interface based TMTC handling<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="-0.028136269449041573" nodeRatioY="-0.08493150684931505" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

File diff suppressed because it is too large Load Diff

View File

@ -1,348 +0,0 @@
<?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.23.2-->
<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"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="174.17919999999998" width="273.1071999999999" x="1000.3040000000003" y="433.61760000000004"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="67.380859375" x="12.06892812499973" xml:space="preserve" y="7.247609374999911">Static Pool<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="-0.5" labelRatioY="-0.5" nodeRatioX="-0.45580882479480683" nodeRatioY="-0.45838992615076934" 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="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="120.0" x="1141.8400000000001" y="536.1087687499992"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" lineColor="#000000" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="86.142578125" x="16.9287109375" xml:space="preserve" y="21.015625">1 x 128 bytes<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" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="90.0" width="164.73919999999998" x="1113.1776" y="451.01919999999996"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="false" width="164.73919999999998" x="0.0" xml:space="preserve" y="0.0">Group 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="15" leftF="14.739199999999983" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 1</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n2:">
<node id="n2::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="60.0" x="1142.9168" y="466.01919999999996"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.0" y="28.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="n2::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="120.0" x="1142.9168" y="466.01919999999996"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFFFFF" fontFamily="Dialog" fontSize="12" fontStyle="plain" height="17.96875" horizontalTextPosition="center" iconTextGap="4" lineColor="#000000" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="78.5078125" x="20.746093749999773" xml:space="preserve" y="4.862813159989173">2 x 64 bytes<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="-0.5" nodeRatioX="-1.1657341758564144E-15" nodeRatioY="-0.41895311400018" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<node id="n3" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="89.99999999999994" width="150.0" x="997.9168" y="451.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="false" width="150.0" x="0.0" xml:space="preserve" y="0.0">Group 2</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="997.9168" y="451.01919999999996"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 2</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n3:">
<node id="n3::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="20.0" x="1092.9168" y="466.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="8.0" y="28.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::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="20.0" x="1072.9168" y="466.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="8.0" y="28.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::n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="20.0" x="1032.9168" y="466.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="8.0" y="28.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::n3">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="20.0" x="1052.9168" y="466.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="8.0" y="28.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::n4">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="20.0" x="1012.9168" y="466.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="8.0" y="28.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::n5">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="120.0" x="1012.9168" y="466.0192"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFFFFF" fontFamily="Dialog" fontSize="12" fontStyle="plain" height="17.96875" horizontalTextPosition="center" iconTextGap="4" lineColor="#000000" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="78.5078125" x="14.814773932043863" xml:space="preserve" y="4.392671444094276">6 x 16 bytes<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="-0.5" nodeRatioX="-0.04942766514963404" nodeRatioY="-0.426788809265095" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
<node id="n4" yfiles.foldertype="group">
<data key="d4" xml:space="preserve"/>
<data key="d5"/>
<data key="d6">
<y:ProxyAutoBoundsNode>
<y:Realizers active="0">
<y:GroupNode>
<y:Geometry height="90.0" width="150.0" x="997.9168" y="521.1759843749999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="false" width="150.0" x="0.0" xml:space="preserve" y="0.0">Group 3</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
<y:GroupNode>
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
<y:Fill color="#F5F5F5" transparent="false"/>
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.201171875" x="-7.6005859375" xml:space="preserve" y="0.0">Folder 3</y:NodeLabel>
<y:Shape type="roundrectangle"/>
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
</y:GroupNode>
</y:Realizers>
</y:ProxyAutoBoundsNode>
</data>
<graph edgedefault="directed" id="n4:">
<node id="n4::n0">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="30.0" x="1012.9168" y="536.1759843749999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="13.0" y="28.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="n4::n1">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="30.0" x="1042.9168" y="536.1759843749999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="13.0" y="28.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="n4::n2">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="30.0" x="1072.9168" y="536.1759843749999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="13.0" y="28.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="n4::n3">
<data key="d5"/>
<data key="d6">
<y:ShapeNode>
<y:Geometry height="60.0" width="120.0" x="1012.9168" y="536.1759843749999"/>
<y:Fill hasColor="false" transparent="false"/>
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" backgroundColor="#FFFFFF" fontFamily="Dialog" fontSize="12" fontStyle="plain" height="17.96875" horizontalTextPosition="center" iconTextGap="4" lineColor="#000000" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="78.5078125" x="20.74609375" xml:space="preserve" y="4.392671444094276">4 x 32 bytes<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="-0.5" nodeRatioX="0.0" nodeRatioY="-0.426788809265095" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
<y:Shape type="rectangle"/>
</y:ShapeNode>
</data>
</node>
</graph>
</node>
</graph>
<data key="d7">
<y:Resources/>
</data>
</graphml>

View File

@ -1,256 +0,0 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD>
1 0 obj
<<
/Title ()
/Author ()
/Subject ()
/Keywords ()
/Creator (yExport 1.5)
/Producer (org.freehep.graphicsio.pdf.YPDFGraphics2D 1.5)
/CreationDate (D:20240209121153+01'00')
/ModDate (D:20240209121153+01'00')
/Trapped /False
>>
endobj
2 0 obj
<<
/Type /Catalog
/Pages 3 0 R
/ViewerPreferences 4 0 R
/OpenAction [5 0 R /Fit]
>>
endobj
4 0 obj
<<
/FitWindow true
/CenterWindow false
>>
endobj
5 0 obj
<<
/Parent 3 0 R
/Type /Page
/Contents 6 0 R
>>
endobj
6 0 obj
<<
/Length 7 0 R
/Filter [/ASCII85Decode /FlateDecode]
>>
stream
GauHqbDmmZP2*dc,(JOJpjOd%&g0Gt.kP?b'JS!oXFWk;Im?:Y1FF&?pX/3EoVU?*%+=B^)P-:.4Rk3i
s*]RdT>/;PVD+YBq=ssLgOFU_/,o=0j/OX---Sln^>2M2IJ`Ed^O5qQVk*kY?bcTdDuVnJr7h6u29C@I
n%\htn,N!oI>bl:q_/!E8Gq*>`4k*>H&qp+S%%gYRm0Q`^J"46=8=LjUt,*E8fY3j6_j1O)dT5Rr;36d
2TE1sT$toUNk^6OlG0M$`a8\SDu][+2SjlHja.QR?iT*F^H[dDqOmlY/O#F`o1-puZR+(6c_!Z$9=T]T
3D@>hs2JCF[r;<4>?W&7(f(C?hGX+g+/RK5>#oRYmB]N&/Qlj"0&n123'X=7S:R]6PWlg21E6kE\4XeS
2\[`<&[K"H.d`i7:KfE2]p+JeNVt]"Y*L+:.OV5^Fb.U2n0a"#gC<8<]$kU=OJ*NV)=du!KCWJ.>a@;!
AiTJpf@'RJ7&^"(Yb"R/l(pRdQ<kVs%?8)!>V`PO_WeB@PWi!pB+5<W>7FTapG>"eLU:Cra(k/I;;^ah
mAcX]0+W@c9"cLWCDs?VR[8F?6<T5F$_0C:m"MjrBBs4k)B)!QUC!)4)NFtV@<<r9q$g4$m3nNi+q1ru
e]XeUoN<]O"rT6rdIc7nDrJXG_<,>V[+c%pmXpGV6L..t<8>20'_?%rO!N4JZZYrn7!-+)W,<%t>ADS8
/TJAA+-bJKcYS>'s3KC9l,0-0e5A(AITl4'3i\f46/U?l%$3[[D1-a[<d(=Q9X^_,!@p9Wm'TZV7GRh3
rF)Ja082PP(B7n)^+A+X7k=ASO*kK6,(6!VRDEO4*]U9%D@#R--_LN6iTq@`,YPD6Cs8m4P?AfT*5Gd]
[;(LpJ)b!=C%su^D3#$WIOeISK@*am.##XK[S[_P!?^>tVXHJ#qEt3?2Z8-=k2XWs.,17T[bN*>#j]U:
+n?FH0^6*EPO[^;:H%KH3f("]U9q;P]"j#B)eLQ$@1?"IBSn;YH6lYb&!tVhGK>8uB'7MF"FFfqpVGc<
e,4hFfV*0s,_P,I_QT;tW4=HSW2;Ra[gBM4`qdES$e#Amc(@.hj5@*2oF=>L%a<mBs"l[h;q(=d*OOlf
J\oOmWJbKB>#1\>#4"cMCBF9nZ_8(\3B3X.Ce5M5@GC=9ETuK##3d.F9`om9\CVZIc4fpE_>+:BO<J]+
H82#q>$E&a":@K&ZD/,"^/e9SMaTXR4:emV+:YVSV']$J([5Yf)XL^]K57i74uu_XpD*@olnX,&NO9ib
%&N3p%WmY0V3,o$NEeT^eb<HbXfh=$96H48FpusINN>n+-?`G3[[)E[c:Y2<_OIriakDd!l>kAQ%VsAW
AF8/If$8]MoQQX3ZSq>[Jc02#p-mQXojmFp?*'U_akBmhOKg^+ZZ`MGY*I2DWtB:KeU(@CG1Jk?KA87s
@g/HOM6nM9!.9`I^:<BOrbA\h]cSgEhCU?uURj'M1$c"YMq5^c](iQ#&>PQ:2.[P-3c2mhE-_@`J$@fS
Dr,`=a@ib&>8l*Uf6+&oBsDgN[$uSRWT9WU_`,ciCF7Mt7gQI=T/'sF(R6T"e#_EoZA5)r#HW*R#p`]_
;dE]PNpSIdg\6CCQ:6F]TV^1VrK%68^6q0\hmJKElGB.7UIAF+V^67qcJ?fV+8sR\g-Z3Xb'QW-m65AL
UC0S+Tsnq;>ibcqXPfO;:H"8jre($\6sJQ*T!GIQ4Lo&VH+V6@U@aE[<9K#^&i7s"S2Z@R:6kOt\?n?\
@p8!#:7CKqp!R+&Y]'#C/Qrg2RMVR=@b(t;`foH&?N-VC;Id]"Fln?G<NR>6`9P]52`L$-C<*l4plc.7
4LX"1<nT+Nr-?OgD0lC<YFkX:nR7D1lb[?95#;tmOVMWgf]B,h03"K'->^:DU1!shS^`%C-di:.>MJtE
:J,\\RN)(L`nK=]CO(1Tdmj\N7gT%#Z1B<^qo0QCdR_QNI?m?+,6&K!4fE0TH4Xp3/#bs@]Ql.@WaMU]
BWhp*L`O1NZaDS.g0J?#[[Y>lJYclJ%HQ=qZk].NXgD>/K%5%V<?,\IWJYSHNOHKbeii>SSm3KlhX7(m
EbVLh^[9V@pJ^+\Sq#\_][o)h+u?TPo1!NNWGPYC7aKRZ"@1!5)Z=%7Gk!')(2ETj@O9&6$UQ$l,W5#f
q92>l8u!Ra*@P4\#";duf5O:q?Lihd1dQS3oQCa.7'^3sQ.A<TLOPreY-r$(%o1.R9pT8^-Ke$C46_+/
`eS`mioh5qd#K&0oVC7:jQWId]7!m5,a&m0hQ=c"ZPH0.p1eq0dAGT7:YaXJ`ZN^=N>&k8^p7rB.2tdh
>7]pt>(T/ji2PU,j)3m?DJX0V4aUHr-0P!<FjKu<`jQ5I@g'TS-p/qo6bA*h`@>K!O@WDGNGfcFPDn8k
]N0g#G1lYPO'jlnkp+!`qs>Ub7G_G4*lj8smU>/I@Y3o^Fm>;3YP,@#_qiT]gE-XV3s"W]nb.b<p$Y?3
@p.AZZUN>E*hXPKbOH9YrARdUTCN!PnOqQaZPu(cT3X,pj&";:8,VKDOZqD-aSKhVqQ$O/25[h-T_6]R
C9+8bQB+N5[;fCAnV-i0_0PGC#EHtl39hG%(g^PAcZlqVGQ/U8AnYrZ(Kg>P)i3=ig+4SW7aRXtd$=q8
L%9Mtd$HV]Gf>!pdr-2@flXdJ(LPtj,MLHnp\kW*SaaSJb1esgYlB$7O7W0)U9S1#a./*sn^kcE-_o=M
I85TH:.DQ2I1D!Gi$Z1lT>/6I"-Du2cT.EHFQ=[6rO+36h85+pMnhi2=P5BW=f<9C4LP$c2Li\nn2Q>/
86aIt6etQI5`[KZ2E3b_<i]HljKMH:KY"CGZ?V7LVr;CX2Oif;5.FFdQb;jG+0?nF_p@U)<,M@*<4`aK
)7I3"r9K'"d!+\r*2V?N3SEg7/[&sW'\#jaP1H<_Znr1[lc&EWhnM0pMdc"_:8QfFUdiam=V#(qFVU2P
6`9k0jf`3QgpUO`EW+<g2@2a5%OX_YV6HW:\If`r(V&+CI%c@aW^)q#c$?X%X2Vsm_OT3YPd"2`l=-kK
Das:U\!CiR(YnG5i>#L&.%$!kq/NSCgUU3Qm"p'\LpGKB":LoFAXH@$VMV`V[,8V9DI=TQ(@-18S2PtA
#p\$$-S5\jm7qD$]fs/,rG[Ujfqmc#bj$fCNm`;<$&dI'B'Vt#8i)P7BZhobd:A7W,=T4K&,FXOip8p_
LOQ5mBJ%hI`BU#`MR>[AM[6@;S!hjY_\ScsBmPt3:"*f2Dc=`_qlG]U3EaMtbh9gYQsI$c->q1Tn^X<F
B^MnK`hQN!$r;KHG.U1*@D,\s_idP]nd8k`lcJB9?)Sci:d<<iAk*aB)c^K%1m?I&*P6=t5d9rE,m2_$
Vn<<o:eoNRDDM*'FcQa361sg4]H*<Ip6FiBg6!taI_2m/9DeN52='`Z"+09,F'QE1KsjB9h>)I?UQK\,
Up?#_#P95ma[2];_gZ=I=Nu-1=+4sjm!L#3.`Ou)]!rM#B/5^beuNBCAU0$?f$DdgQ%CA>f7M`$=0J%X
#SL]o3a$TV'C?#TA`hfd@]!MG@]tt`ak0Va-H6NQ)O3e[.sNgi/Z@KEodYrunT!k2)NU0T2=8&]Z&LD;
c<'Bue0<IHeB2N,>,\p9X>jgXQ&<0+A6*E=(@bpE05=Rp<EU4MM1k"f?*Z'J_JWF3?gT?.Y$SZE.+,Ca
JE/T+Z'D)kYS9lJ]rKo2IL8aF+jlujC-9UUnMXTFe4`&:/f&qAB\Iin/1n_3=D--;0Ul[BZ1&_8Db2^b
['Hk%40co"!"29l%*"Z3rktlWD2eW.9%^ZRNk3u9Ma+0YGen(&OLn,.GrJr91gVYDbJj.Yg8UN4YgmP3
Y!GJBV;Jg7ITqVTi[KWJoL/WD1pI$.H@n./I[Wi"]__D15CcfGaQP!u1TR[g9*6Yui6*Kp(MI7$T^Bl4
l4#-LRH0"HG"N)]RB]N\G0l5^ge]fV#>g27[#Sj(5X\1A7I["a?*hQ&War9\^@fsYo-*5J)#@>mOb#;?
;rr^6]^*0nWSp<R?kd:ah*%WZO-=Wa0k[Tf`^=_Ta#4<ck0d5oZlbe`(65[J6f8.>=Y/`2oR4nkGg!L3
%J;@oKhW'B3<Gm]mR*t:^!&E,Pp]&mJ/lP,])<P^q`ktSqPu_Ah:VB"opQE8:i>@QNd)%R?^]@8KY8]$
.r78lfYIc>Z5b_;0L8GRq9"\nSE.(2XlM$>`^k0:f5@FBorM*W69'%!m.skVQ$M@lE?!AMY-NVO\nH:c
I]NFkbrYC#-2CHlUBd/88?o[?IRlfq>0bkMj&cg6/OrroTlBHl3H3lDAfm4L&<hpKl\%T0QiHDJP2GYt
U4`DcUq^i\OV:fom2J'(`3G#k[*W\[L/r8*/XHqTASA"9I+f,)`E[XCaqGQPOc=m_YU=#U$X\O5;D-bN
)KjiEYJCg\-^#-k8sCYcRC4CWSN<^;C2_WT">0?Ci>16s["nQ],:-2g"!];L8L@r=lHRtr7#oB4OWs60
q1)H*ql\Sf6AMaE!paTSP<K+=347A-D?f:";sM31R/=_i`c*-TWc/n!ra];sAjc'/K%G]7C_t<Z&M3N\
o2O3_Bl#c^,'p/l2gC*(nr,[5&7Q7Yns@;$M!\[u0nNi882R-/,!R5mP_*HroaS(^qChALk+Wk-X)r5T
L&+!bYbJHM\<[mH\6b2=ZQcl@#jk0<SaoR\E`Q-QN&@sqofX7`lNXo;FpmT-hCRaF]Ma><OaX=s8_V`T
]d`0A9isrCnq?=ce?6$HHMJib`Y-?ooV$!Vh;1M(#kM0aQFH``Rk2hFk/$UJ[U'l"_q\sAX\5RrVE@Z^
Mk1`ZpI_NpdE*>q]jT*IDlNbQDjG._M'_?Y`&ZD]98h)+.paUXcgoPB\@KipB8heU`Q9djs)G2Sq9W]T
;!cin^gFr9$)@J=Hi8H`c,WVn[9%;6O7i2O^*efCg"R1`h]1Y;,lHbK%)+[AqJWJ39CJJ9qBI'trTSA$
SK+&//4\jh6tD.*^G[^]c/)S#$%)V_mk9Z2Z+G_DK;)r/='roX^-%4mLQ5c)]S2rg*Ui2]A4I`kOEEfP
rP96Qn+P%L+/mr]/_^N\Y.DO?ikpHkqrupaO,_jm?!tiu+:HrYnnnmO6a`q30V$j_(GT'QV)'H6A*ldc
]n+r.`R3,V1mAT'D>#PR[kEgOTr$k+/H):Z7Vdi2^R.pdMTcN6WlRK1T?9+C,m@a`P-A9`=38dnZBTZd
cP=(*]@"kFZb#iW6#VQ4_d\oA&(DIWh&*J,mg2-5Pp>"RL,A(Zd9eoZMUH5TY[Zo+774bhDLhCRQZiSA
Q=Qr+h0$%kMf@YrO7f&KrOCIsG@W80QL#&Cb?3Jf'#W#5?RR#ZMAYSa+X:q8PbO[]5KbWkHIi\YIN:MJ
bPjZfHf0IcRJ\7Ioj=lmHp7QL118I/*6;M[ZcG.!PK775p!*trXbdXWmgIVK1/'rqmlP?%X,0V!fFZaW
pAG0$FOWBnG[M7jBoj-eftY.@*`$C;leS_WVs1m,cMY1Ko"+c:%A)'?1\jOeY[1U.l-eL`?eCJ%D:aZ=
%1tOj_N@\O"pcgtpjni0>?sfuP%g;u*1LRhLU5?d="2]P4<Da`>A)#?>K]bs8g?;P!F\B1SoqS!Vtd_+
+KXRk,Qm>c]!^n6[9Gh^q^+A2WmWG)B=8a0?9-NPrM"IH^MUs9NV:R3Y49T!?k];[]8upK4&*tHAr[8M
Kda\eZ1(J+=fk<41YLXa_4(U?Q]QE![nEsZ0dr`,?\/(740cZ?,#.CFA2@`.4f3C`Wjr,m=6d82hPNFq
X4(6OR`5)FgKD3k_no,.?f(lZHhMh]P0Ni-&SjRBLJJe:Ki+JD2"tn,XDnjOEdY6U\3=gNQa_IoJ)fNp
/gPaQeORpaB,oM"hd8$^EQX?1bhWl3"R_K$o0eYZEl0WRKCRSN<WZaegA*;"'Ve\8,$e(3%Ai$21/.O.
CcC!NMX!(O>K`08#kG8R7U0(4Hu16/[/6NY1=/g5+-L:lnoT/.XD[)k8BfhY:P-c6]8?JgYLI7XbY6[[
roOisYrC=ZUfPC!\L[[mk]nkB'!Z=8F#ELjOaDr?6Z^n*W*PkEY!n4!HXsJ1PeHQ(?uEmUNTb%kS2aE`
/00(<b-Y2Ec/>-]8jqg8]Zs.iNL'iq(bpCX1H2(@*UT;Kd,9P<;4Njo>SaWsb]p65_n@Hn_>/B:iP%k"
QbUGtF[(CX&(TOH8baq85o.GU=3dT2r/gCWrFh]eS336gbE25d0bEqP?2/2Fb;^GYm!%J:bY6VTaImZJ
`@(lW',Y-"KJ/mg&R![Bb.B0$Fh>"V3TB#"-%$/O@-Ai_)am0X-r3]@m.FYVbb0d&)mYF@]7ornZ"^uH
*)62G&'m*Fj1F$!Yfg0c(Z.SELp8da&Ij(B"C3.n;/)B>>Vt0kcI?+-a8-aA?#H&WZu$DEPhBtd*\3/B
9TDO0^nR5b;=US1T1%]E(VK:h?b6/V@FP8.CHe+OalME4S^0%)k"kc$o>9/5T]71+Qam+?7[o;7;r[uM
IX9Gd"`tp-\DpHO4uhT]n?V):Xg!!?#;\0u;0Bh1.77$G#/0$?MR^:C9i[Lq<Wmd2Br^U2]5\YY6egKj
pptXT*knOP.DBjDpt@:Xqr#.8V656ensl7ts!SfiSo9H%LZ]12.=Oe!S,La8p',2;qZabrhRa-/\G"@h
F]g('HASO0Q]I\$A0I"%AU`G._[K/s#\&KV-'"Y,h:#m=9>1W),\Xh$k,]d(7=HbkN1NS'[uk?NA<,W7
#`5Muq!/]:LCJIIghG0P7si>'2'O-PEC?"k&8sHOb0<iij1r+L0L"nP&4U=nVQ+=t*b!Z<cE(J:4g?PU
7/!@so4gA1AMQ!$LaNcjXuo5'<ZRNIQ8TqW#+4jj5PQ]biLAeW0ADi+n0k#$W_lBl3=fl1Gg=?=(JV/K
j-?KT3d;r.`S),CA#&+2&KXMH"Zo(?jZRFn\",*kB$Qt;O%:;:Et)d$(ZX*96I[0h@7Z/d]9OoL/JW;"
pIoB:-S^Mu%`YMXam??RE*X-$jK8IK2D@p5_&(s7g?Dh<TR=]:9kE_Zb.+u0Hm#g)*:7NEBp=&kk;^V>
L4-7]P@lBsg(h*0d8sL]]4"5`Y`N"r?349-dU&1*.]aSICOB:PKi0r'mq\\;V*t"Y*ZX-D)kFP:G-PSU
^Ea<VoRBh400\iI^U$@:L!FM5ib6Jf3PYL0$qcn2:*[b?VZFGO(o[JSs(hmNIHP_Gp3QSuf))Z?5M]Om
oi>G5eQ;)4=88EUXhK,IYMS,GH1tWp=)[WZ>CQ8=*m8mOiudM;^3Y]eYdZi[q;Ucj/&Pe6N5s;4@4M%i
#'ed,roNh1=S;^$bh&I>fAFf"Kq.9!7,=GuKQUYDg:\jF]G5l=QkO])Q;EIK7#WMQD(NU;hm_Xfj_8mV
GHng4Kcf-fLEp&9WUV-WJZNb"8pu3L9d8P2[ji$J;-FHdSE%;#3CZs3Bt!*i*dSBu6VHOhi1XMmeKl=V
0QjE(U>DSRoi4I$B6LgdQAGmPb3hD;P1_g@`&[4C:!-?3etmbZl9$sb>KjdOZdnTk!:R9t&YsjH:ER1D
/Lu0Z[gCt\e9OCec8[<e<@%a[He^lN\MNmgjDh1eX"(5f2c(%;3@jDR=si[4C!*+r8&rh3H$&.8GIiao
<e/g9U-c/QCjTaGhGi3pR.Un>X]D8Z.G@kD4MN2/f5e2BdV8k1CsA+>aMJs)e)nHU\*g.\*`TW@,gL\.
I\U'"Bb/2M!S4PL#LM+H<pnMb$Z66DKbghd_k+X/`YmiAB]6q^'(<'he4o%a.#iNf0$HR(VS-ji)-=Ao
jL]lj>%Mt\s+[k]H)eU!T\4h.aq+=HILYd=jo$*_IOQQC`]SXB5-e0KS@IRu%^05]IqW]Ik_YAoa6ruC
Y(<$kM-6I[1L*'"/tQttcaO_:[u9G(`UiK9oBkQ5G4Bs,nU.rn^0NkY?9I*tSo5Fk[hmB"Ms&j6oBjNg
mGdqbnU,]RI(OX;>s."niq`<-q=`hDG4BslGeUk9^0NkY/tQu7So5Fk[u9G(Ms&j6oBkQ5p>YmknU.rn
^:ed*>s.!sSo69RD\pa/Ms&iQkM*uImGds8iq[cfI(OX;]m.7s4Pqc_D_I]#(XKRKkM(pXgVrdOiq`<-
q0):U]6M''GeRHGh_(L=0:m(ucaShqgPb7L`UiKVq=YKJpWg1->U=C14-o2E#/m7[#'sc)YOFFY7JP-u
HBh2=THn&jh*S3M>YciqUiuA":/o"lc7VuV>3DHA`"?1kPKR:0\%=D7/d(kRGC+T&4Lr,Bn&"It/sNg1
ai9l=m4&a"?9?gR&fphF`\V,&jHQ78);R9XZZfVL0qMQbi1"&`ncr#(3bCmYNU>F?CV<XN13;oSk@41S
%.4jRIj<=6n\P/(&pCsLOM0JhnGUiFn)^>\\a$aM<S]MRN`)ud]A8E%Qc46s'!iY/i*q;L/]?7Z5;FQJ
LZb<"dmJ[6_>/B:^-.=8'_eq=D8:Kj.o3UL);$SmG\BM-gQ9BV\/Mdn*6;(J2K-<:j`c,sj>.6pD-ZlO
f;`1a\-j7EL/sOB4Vcca2Bqa``Y(YL$+l[D7(kNW<-Ln1PTua5ed-X.82ua0l-%aS(MA$,IC3"GeF:i^
*#TY9HrGhsbZBl)`Xff/;Z.D<2EPi9=VtE"G,s:m/<fh3%`L(O9:=Hk*D\jjT&tB/L::FBel[F^Z#0(u
MQtl0FJJ/M<34KpjZRaNdU.kN7GY?VI#ME+IH*HmrV!CN:&eS(q7mE`Y.*Qq,'h&U,C.&@&NZNJG^D%#
m@BA]2r;bKr&@A]AaS'C=d5Pr?%0-N=d7(,2t+fT!nl8=0]bOOSV'l?\T-*bCkk)4EL+)[g)9YE9kAE^
f_o@:F%%N5p?/DkYjpEu[jU2(BVAD'UdB1j8U093l,e72oT7<:RX1<a\*WZk[>EYarEV;@pSg)Qao7Y:
m1%FVp5N35in2CNg=VSEkVX<'Z?GMpfdU+Gb*jVHYTN4id-:,UED)PVQ.o55"((qj:WYa+)XmGjAtcGW
%?A4eOf9A0e+UlN&]jQXLLpb9./n+[h&JS#_U<*.q=,M.OO).[;hMhl_[)o]ZhJTpB#3=X%nV_7pZR3L
Wlkd0+gPu=5ku7*%,p]o@Sj4NGqq,<h4[E>-[1$o1[Lt@AP>%GXU<C7;OkPi?,!!+.WNXJ%!p+1<0Zq_
7nV)Y1u?OdH>Al9rGq9"&j#$ANchp;D^L4?/0NeR%%188c;K-&p-@&acD.qE'RmnI]9fVn+[\;XGtBuC
Q,XA;\,0:^<=4b)e$V%1QLHnYgqutDB9oNQ!,q.nMASbLEdAr3N2=#;^h3RH><DIAn.iAW9XK@!7SS[;
O&E4EH:MB-+WfL)oO>R>@E:T?8($'+Y3@LFGC_Kh,O0.kp1&#qQjbrcNO%XTqU-P^)(\B5Y`R;B?348B
.c]<[;gs_RTlFVDdt*7d=3"D^b-Enu_H^Eu]OK->Yh'js;YD^Qjlj?VkP*Bt\o50TMQC2E'f]]F4'Ptf
nrGSTaN<L42@AQ]?]M&Epe.^bs!VAV?G6#5DuJ5_]D(m.HL#cn=O"H:iqYN[n%Yp4QR8.!g:cT4VTD&e
%E!D,l[-[THu9SrEa/k,f4Y%@<rG69)tmpfos_89q!rUaL!8>2h)\R=hj7aH)l*CPIsYJ12`BLL?A-'5
+"c-sf0A5m;ZQ-uEVA/A+jh6UIEQR,N5P3M^L$IPG=gE7(NW`\47W\IbH$'+^O*&*pW.mf7mR*hfU's?
K`.hF_:h[jjk`4oT-pf8ASdc`2fpean+(iiDin.+1TR(GfQX_=+sXSG;!s44<W)-98'`Z6A&qKeCk[l@
(5[#$>FLJD*Ye9PUXi/@RId:(/S9.pmbo_8aP]0Q"8+TtpMbdDZ1$2Afic#>a`]3'-V$7WKFfNW;V633
2=XFZqoW#b`&Gn<njm3IkQXZb&EQ\S*]`)RYS['?S/_D"(A._H,&bH'VI;o&l_X'o\S8,Q44Sc5_<-l,
[md53DP!2n](+VEn0Zk!g\8T!&"U@9Iq,j?gH.C/Fec1]hs5:ZXj)4/Q[b6Fh-HXKUkg^D_eFa!i]V58
*kkJCnPq8?g>f6HXZ*PEkM?uX;Ob:Pr%;c#-V>2RC\#=heh7OB+@r;TkpP/GHW@I8G1Nhs<4>g\NN@A9
ioX\0$YQrA;I'2JFGs4rRCreKoZ;>3f5kM`^fIdZfdH(][0=V5'k@4=9GPim%[rkq=>L0i6,)C;)s2a,
<_nrM'8=1ZG##F5M1<NOhnCaM(q/;/BCINllnJ>P'`4bM@H7bBf)]^<fV7TjL&8GUEf7J_D<HWl/@heK
J::T-QF>S',Q?sMEPkAYK>ALUEMUM3R^./2c)k,Rh=A]hkI017ctbj<LTQoc=g5MlYW%&ec7o!#ICj3,
=TWN3LL'3]o>Y,44q2:/hQ5SESjM93'PA=T;=OH?Pd"E`]-VO"/>ZZ&Da9Aj>lH(%I:$gNnU&JSiWJFn
^l:&f`kSA12b*02ijsWg`P*u`9.hUr(C&`O%\CVN]tqE92XBU3hW`SmhagF)\Q@&W4\ROiDj^:lJ/'gp
]n!tuXn^XeH6h4Dr<gLT__OhK`4m'Z)Njps@!"F:=^>.79#LI8T3Tj&r_Mqaen_bN;%`os?eQ]apr*_*
=W*u&]@^aF"'<6-mMD<5CF<Th]H=!7H;Zi(g,1I]=)*s4SbcnM$0)oooD0a0=khF0IrU0glAD!h+QaXc
bSJjrp3ZiPV+O6l@_9):L3>[j$hBkiMuCQi&CW8m0u_^p,:1R`Z/$Whj@(J,Om!MtZU5r4;^62;Mdni]
XW_V`DgPVn>M*)EWo&hh"tRFKO[UPA;,)@Zir!eCm_G[_f;:MlD`kI847&Q*>1Q;XVIT;7hiAaZA,(3t
i!/ZUEE=!#QV21OO:LV)r!c<S-4XR/mIi?oYDoU\`mNH-`_G+40P#)/YbueY-Is"\ZP_AW7;<'&$:V_C
__G\N6aYp1VrqudM%^EF!)FAr;)ud3U_W@pY"rXZ$MPbDh?3:cTtOVD<c4mQk1t:\DFH^.@T9jVFVdNn
+h]W5I\'prGX8lu.-0o/i[rcj2:gZpluJ"p6dM#PIB8Lj1%;-LV73TXmOK*2r5+Ef(k7_Cqngcg]$G]@
<PR10cGpME\+<bWEUo"EO0p@T:Dh:a*=mHkPOV\t#/(f-jBHgJ'W%"l$Sa7MO4$R2Sl&A4>14)<bhE"4
V6bGbMUIn$<H720g<Uh"C7#r09mMV4MDX'=(<JsVbDLt1f+80M=:b.5MtNsmm$6egP@iF\@rpKEEA2<K
.Ul:<O+[%G@HOG)IRh#>rR%\>#&'Y+#,7i==$b\PQcd4NRfYq9\fRaZ$SkhkOMY3O:A_jZ(!nCY\*D"j
;r'Tr,F;<uH^%aplh;I4CRi+n7c9lZe.+QRHeG$e,o2!2"a7J'AZQu&8]iF/BFjpL7OD2>k(k@tI,)m?
&Do[H)F@eSj$srLMmb^cBP)bmn>6uD`s<bGn*n72+u^")W85\_<pomAn5nli)&+7HWpms/&WSUYU,f&C
#5Ac'b6FZf]Bth(A]g3ZQ;6$>CcPBH_iW^Q&9OpeP^i8RATr]Y8RAZK;&i.\MQGHi*^P7lUqYf9B`/I\
qZ8&2`u8jM;rLjE<=4aV*i+Z4\od.U6pol:BW7($Ur,NE1[b*JD)#'+91GP5KBnu9,J!g<bt<HTar4PG
9"SP)e4Vp:.G?(tDgW`&KH3L"=1F5#f-pPdc#M5b<3Yg0R?d&rcH`u^%DL7/j%m?]0<c#"Z\j&8A=Z>>
i>j2R^V<J)bmI8.G3]Ran2u^b7T[MIlijF^3R+SfBA72s/?(G/3)h87Q"'1kBs7D/5>A6+\`OTi[<lmg
IecDbl*!]>gnF[RrpflHVtto@Tle->l0J>$j)m_ArI._+p*8=1b\E&As5i"M58+"hg]%MM_N%=~>
endstream
endobj
7 0 obj
11983
endobj
3 0 obj
<<
/Parent null
/Type /Pages
/MediaBox [0.0000 0.0000 311.00 209.00]
/Resources 8 0 R
/Kids [5 0 R]
/Count 1
>>
endobj
9 0 obj
[/PDF /Text /ImageC]
endobj
10 0 obj
<<
/S /Transparency
/CS /DeviceRGB
/I true
/K false
>>
endobj
11 0 obj
<<
/Alpha1
<<
/ca 1.0000
/CA 1.0000
/BM /Normal
/AIS false
>>
>>
endobj
8 0 obj
<<
/ProcSet 9 0 R
/ExtGState 11 0 R
>>
endobj
xref
0 12
0000000000 65535 f
0000000015 00000 n
0000000315 00000 n
0000012726 00000 n
0000000445 00000 n
0000000521 00000 n
0000000609 00000 n
0000012702 00000 n
0000013180 00000 n
0000012896 00000 n
0000012935 00000 n
0000013037 00000 n
trailer
<<
/Size 12
/Root 2 0 R
/Info 1 0 R
>>
startxref
13253
%%EOF

View File

@ -5,14 +5,7 @@ High-level documentation of the [sat-rs project](https://absatsw.irs.uni-stuttga
## Building
If you have not done so, install the pre-requisites first:
```sh
cargo install mdbook --locked
cargo install mdbook-linkcheck --locked
```
After that, you can build the book with:
If you have not done so, install `mdbook` using `cargo install mdbook --locked`.
```sh
mdbook build

View File

@ -17,14 +17,14 @@ it is still centered around small packets. `sat-rs` provides support for these E
standards and also attempts to fill the gap to the internet protocol by providing the following
components.
1. [UDP TMTC Server](https://docs.rs/satrs/latest/satrs/hal/host/udp_server/index.html).
1. [UDP TMTC Server](https://docs.rs/satrs-core/0.1.0-alpha.0/satrs_core/hal/host/udp_server/index.html).
UDP is already packet based which makes it an excellent fit for exchanging space packets.
2. [TCP TMTC Server Components](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/index.html).
2. [TCP TMTC Server Components](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/index.html).
TCP is a stream based protocol, so the framework provides building blocks to parse telemetry
from an arbitrary bytestream. Two concrete implementations are provided:
- [TCP spacepackets server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
- [TCP spacepackets server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
to parse tightly packed CCSDS Spacepackets.
- [TCP COBS server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpTmtcInCobsServer.html)
- [TCP COBS server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/struct.TcpTmtcInCobsServer.html)
to parse generic frames wrapped with the
[COBS protocol](https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing).

View File

@ -11,8 +11,7 @@ time where the OBSW might be running on Linux based systems with hundreds of MBs
A useful pattern used commonly in space systems is to limit heap allocations to program
initialization time and avoid frequent run-time allocations. This prevents issues like
running out of memory (something even Rust can not protect from) or heap fragmentation on systems
without a MMU.
running out of memory (something even Rust can not protect from) or heap fragmentation.
# Using pre-allocated pool structures
@ -20,47 +19,15 @@ A huge candidate for heap allocations is the TMTC and handling. TC, TMs and IPC
candidates where the data size might vary greatly. The regular solution for host systems
might be to send around this data as a `Vec<u8>` until it is dropped. `sat-rs` provides
another solution to avoid run-time allocations by offering pre-allocated static
pools. These pools are split into subpools where each subpool can have different page sizes.
For example, a very small telecommand (TC) pool might look like this:
pools.
![Example Pool](images/pools/static-pools.png)
These pools are split into subpools where each subpool can have different page sizes.
For example, a very small TC pool might look like this:
The core of the pool abstractions is the
[PoolProvider trait](https://docs.rs/satrs/latest/satrs/pool/trait.PoolProvider.html).
This trait specifies the general API a pool structure should have without making assumption
of how the data is stored.
TODO: Add image
This trait is implemented by a static memory pool implementation.
The code to generate this static pool would look like this:
<!-- Would be nice to test this code sample, but need to wait
for https://github.com/rust-lang/mdBook/issues/706 to be merged.. -->
```rust, ignore
use satrs::pool::{StaticMemoryPool, StaticPoolConfig};
let tc_pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![
(6, 16),
(4, 32),
(2, 64),
(1, 128)
]));
```
It should be noted that the buckets only show the maximum size of data being stored inside them.
The store will keep a separate structure to track the actual size of the data being stored.
A TC entry inside this pool has a store address which can then be sent around without having
to dynamically allocate memory. The same principle can also be applied to the telemetry (TM) and
inter-process communication (IPC) data.
You can read
- [`StaticPoolConfig` API](https://docs.rs/satrs/latest/satrs/pool/struct.StaticPoolConfig.html)
- [`StaticMemoryPool` API](https://docs.rs/satrs/latest/satrs/pool/struct.StaticMemoryPool.html)
for more details.
In the future, optimized pool structures which use standard containers or are
[`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html) by default might be added as well.
to dynamically allocate memory. The same principle can also be applied to the TM and IPC data.
# Using special crates to prevent smaller allocations
@ -68,7 +35,7 @@ Another common way to use the heap on host systems is using containers like `Str
to work with data where the size is not known beforehand. The most common solution for embedded
systems is to determine the maximum expected size and then use a pre-allocated `u8` buffer and a
size variable. Alternatively, you can use the following crates for more convenience or a smart
behaviour which at the very least reduces heap allocations:
behaviour which at the very least reduce heap allocations:
1. [`smallvec`](https://docs.rs/smallvec/latest/smallvec/).
2. [`arrayvec`](https://docs.rs/arrayvec/latest/arrayvec/index.html) which also contains an

View File

@ -23,11 +23,11 @@ Some additional explanation is provided for the various components.
The example includes a UDP and TCP server to receive telecommands and poll telemetry from. This
might be an optional component for an OBSW which is only used during the development phase on
ground. The UDP server is strongly based on the
[UDP TC server](https://docs.rs/satrs/latest/satrs/hal/std/udp_server/struct.UdpTcServer.html).
[UDP TC server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/udp_server/struct.UdpTcServer.html).
This server component is wrapped by a TMTC server which handles all telemetry to the last connected
client.
The TCP server is based on the [TCP Spacepacket Server](https://docs.rs/satrs/latest/satrs/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
The TCP server is based on the [TCP Spacepacket Server](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/hal/std/tcp_server/struct.TcpSpacepacketsServer.html)
class. It parses space packets by using the CCSDS space packet ID as the packet
start delimiter. All available telemetry will be sent back to a client after having read all
telecommands from the client.
@ -51,13 +51,13 @@ services. This currently includes the following services:
- Service 1 for telecommand verification. The verification handling is handled locally: Each
component which generates verification telemetry in some shape or form receives a
[reporter](https://docs.rs/satrs/latest/satrs/pus/verification/struct.VerificationReporterWithSender.html)
[reporter](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/pus/verification/struct.VerificationReporterWithSender.html)
object which can be used to send PUS 1 verification telemetry to the TM funnel.
- Service 3 for housekeeping telemetry handling.
- Service 5 for management and downlink of on-board events.
- Service 8 for handling on-board actions.
- Service 11 for scheduling telecommands to be released at a specific time. This component
uses the [PUS scheduler class](https://docs.rs/satrs/latest/satrs/pus/scheduler/alloc_mod/struct.PusScheduler.html)
uses the [PUS scheduler class](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/pus/scheduler/alloc_mod/struct.PusScheduler.html)
which performs the core logic of scheduling telecommands. All telecommands released by the
scheduler are sent to the central TC source using a message.
- Service 17 for test purposes like pings.
@ -65,10 +65,10 @@ services. This currently includes the following services:
### Event Management Component
An event manager based on the sat-rs
[event manager component](https://docs.rs/satrs/latest/satrs/event_man/index.html)
[event manager component](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/event_man/index.html)
is provided to handle the event IPC and FDIR mechanism. The event message are converted to PUS 5
telemetry by the
[PUS event dispatcher](https://docs.rs/satrs/latest/satrs/pus/event_man/alloc_mod/struct.PusEventDispatcher.html).
[PUS event dispatcher](https://docs.rs/satrs-core/0.1.0-alpha.1/satrs_core/pus/event_man/alloc_mod/struct.PusEventDispatcher.html).
You can read the [events](./events.md) chapter for more in-depth information about event management.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -7,16 +7,3 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.1.2] 2024-02-17
- Bumped `spacepackets` to v0.10.0 for `UnsignedEnum` trait change.
# [v0.1.1] 2024-02-12
- Added missing `#![no_std]` attribute for library
- Fixed unit tests
# [v0.1.0] 2024-02-12
Initial release.

View File

@ -1,28 +1,31 @@
[package]
name = "satrs"
version = "0.2.0-rc.0"
name = "satrs-core"
version = "0.1.0-alpha.2"
edition = "2021"
rust-version = "1.61"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "A framework to build software for remote systems"
homepage = "https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/"
description = "Core components of the sat-rs framework to build software for remote systems"
homepage = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/sat-rs"
license = "Apache-2.0"
keywords = ["no-std", "space", "aerospace"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-support", "embedded"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
delegate = ">0.7, <=0.10"
paste = "1"
smallvec = "1"
crc = "3"
satrs-shared = "0.1.2"
[dependencies.smallvec]
version = "1"
[dependencies.num_enum]
version = ">0.5, <=0.7"
default-features = false
[dependencies.crc]
version = "3"
[dependencies.dyn-clone]
version = "1"
optional = true
@ -68,8 +71,11 @@ features = ["all"]
optional = true
[dependencies.spacepackets]
version = "0.10"
version = "0.9.0"
default-features = false
# git = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git"
# rev = "297cfad22637d3b07a1b27abe56d9a607b5b82a7"
# branch = "main"
[dependencies.cobs]
git = "https://github.com/robamu/cobs.rs.git"
@ -109,7 +115,7 @@ alloc = [
"dyn-clone",
"downcast-rs"
]
serde = ["dep:serde", "spacepackets/serde", "satrs-shared/serde"]
serde = ["dep:serde", "spacepackets/serde"]
crossbeam = ["crossbeam-channel"]
heapless = ["dep:heapless"]
doc-images = []

9
satrs-core/README.md Normal file
View File

@ -0,0 +1,9 @@
[![Crates.io](https://img.shields.io/crates/v/satrs-core)](https://crates.io/crates/satrs-core)
[![docs.rs](https://img.shields.io/docsrs/satrs-core)](https://docs.rs/satrs-core)
satrs-core
======
This crate contains the core components of the sat-rs framework.
You can find more information on [homepage](https://egit.irs.uni-stuttgart.de/rust/sat-rs).

View File

@ -19,5 +19,7 @@ Checklist for new releases
# Post-Release
1. Create a new release on `EGit` with the name `satrs-<version>`.
1. Create a new annotaged tag and push it with `git tag -a satrs-core-<version>` and
`git push -u origin satrs-core-<version>`
2. Create a new release on `EGit` based on the tag.

View File

@ -23,7 +23,7 @@ use spacepackets::{
tlv::{msg_to_user::MsgToUserTlv, EntityIdTlv, GenericTlv, TlvType},
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
},
util::{UnsignedByteField, UnsignedEnum},
util::UnsignedByteField,
};
use thiserror::Error;

View File

@ -9,7 +9,7 @@ use spacepackets::{
pdu::{FileDirectiveType, PduError, PduHeader},
ChecksumType, ConditionCode, FaultHandlerCode, PduType, TransmissionMode,
},
util::{UnsignedByteField, UnsignedEnum},
util::UnsignedByteField,
};
#[cfg(feature = "alloc")]

View File

@ -13,7 +13,7 @@ use cobs::{decode_in_place, encode, max_encoding_length};
///
/// ```
/// use cobs::decode_in_place_report;
/// use satrs::encoding::{encode_packet_with_cobs};
/// use satrs_core::encoding::{encode_packet_with_cobs};
//
/// const SIMPLE_PACKET: [u8; 5] = [1, 2, 3, 4, 5];
/// const INVERTED_PACKET: [u8; 5] = [5, 4, 3, 2, 1];

View File

@ -18,7 +18,7 @@
//! # Examples
//!
//! ```
//! use satrs::events::{EventU16, EventU32, EventU32TypedSev, Severity, SeverityHigh, SeverityInfo};
//! use satrs_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);
@ -412,10 +412,6 @@ impl UnsignedEnum for EventU32 {
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
self.base.write_to_bytes(self.raw(), buf, self.size())
}
fn value(&self) -> u64 {
self.raw().into()
}
}
impl EcssEnumeration for EventU32 {
@ -429,7 +425,6 @@ impl<SEVERITY: HasSeverity> UnsignedEnum for EventU32TypedSev<SEVERITY> {
delegate!(to self.event {
fn size(&self) -> usize;
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
fn value(&self) -> u64;
});
}
@ -565,10 +560,6 @@ impl UnsignedEnum for EventU16 {
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
self.base.write_to_bytes(self.raw(), buf, self.size())
}
fn value(&self) -> u64 {
self.raw().into()
}
}
impl EcssEnumeration for EventU16 {
#[inline]
@ -582,7 +573,6 @@ impl<SEVERITY: HasSeverity> UnsignedEnum for EventU16TypedSev<SEVERITY> {
delegate!(to self.event {
fn size(&self) -> usize;
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
fn value(&self) -> u64;
});
}

View File

@ -20,8 +20,8 @@ use std::vec::Vec;
/// ```
/// use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
/// use spacepackets::ecss::WritablePusPacket;
/// use satrs::hal::std::udp_server::UdpTcServer;
/// use satrs::tmtc::{ReceivesTc, ReceivesTcCore};
/// use satrs_core::hal::std::udp_server::UdpTcServer;
/// use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
/// use spacepackets::SpHeader;
/// use spacepackets::ecss::tc::PusTcCreator;
///

16
satrs-core/src/hk.rs Normal file
View File

@ -0,0 +1,16 @@
pub type CollectionIntervalFactor = u32;
pub type UniqueId = u32;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HkRequest {
OneShot(UniqueId),
Enable(UniqueId),
Disable(UniqueId),
ModifyCollectionInterval(UniqueId, CollectionIntervalFactor),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TargetedHkRequest {
target: u32,
hk_request: HkRequest,
}

View File

@ -1,9 +1,7 @@
//! # sat-rs: A framework to build on-board software for remote systems
//! # Core components of the sat-rs framework
//!
//! You can find more information about the sat-rs framework on the
//! [homepage](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/).
//! The [satrs-book](https://absatsw.irs.uni-stuttgart.de/projects/sat-rs/book/) contains
//! high-level information about this framework.
//! [homepage](https://egit.irs.uni-stuttgart.de/rust/sat-rs).
//!
//! ## Overview
//!
@ -34,25 +32,19 @@ pub mod events;
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod executable;
pub mod hal;
pub mod hk;
pub mod mode;
pub mod objects;
pub mod params;
pub mod pool;
pub mod power;
pub mod pus;
pub mod queue;
pub mod request;
pub mod res_code;
pub mod seq_count;
pub mod tmtc;
pub mod action;
pub mod hk;
pub mod mode;
pub mod params;
pub use spacepackets;
/// Generic channel ID type.
// Generic channel ID type.
pub type ChannelId = u32;
/// Generic target ID type.
pub type TargetId = u64;

View File

@ -1,10 +1,9 @@
use crate::tmtc::TargetId;
use core::mem::size_of;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use spacepackets::ByteConversionError;
use crate::TargetId;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModeAndSubmode {
@ -48,12 +47,12 @@ impl ModeAndSubmode {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TargetedModeCommand {
pub struct ModeCommand {
pub address: TargetId,
pub mode_submode: ModeAndSubmode,
}
impl TargetedModeCommand {
impl ModeCommand {
pub const fn new(address: TargetId, mode_submode: ModeAndSubmode) -> Self {
Self {
address,

View File

@ -10,7 +10,7 @@
//! ```rust
//! use std::any::Any;
//! use std::error::Error;
//! use satrs::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
//! use satrs_core::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
//!
//! struct ExampleSysObj {
//! id: ObjectId,
@ -51,6 +51,7 @@
//! assert_eq!(example_obj.id, obj_id);
//! assert_eq!(example_obj.dummy, 42);
//! ```
use crate::tmtc::TargetId;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
@ -62,8 +63,6 @@ use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::error::Error;
use crate::TargetId;
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
pub struct ObjectId {
pub id: TargetId,

View File

@ -13,7 +13,7 @@
//! ## Example for primitive type wrapper
//!
//! ```
//! use satrs::params::{ParamsRaw, ToBeBytes, U32Pair, WritableToBeBytes};
//! use satrs_core::params::{ParamsRaw, ToBeBytes, U32Pair, WritableToBeBytes};
//!
//! let u32_pair = U32Pair(0x1010, 25);
//! assert_eq!(u32_pair.0, 0x1010);

View File

@ -1,19 +1,12 @@
//! # Pool implementation providing memory pools for packet storage.
//!
//! This module provides generic abstractions for memory pools which provide a storage
//! machanism for variable sized data like Telemetry and Telecommand (TMTC) packets. The core
//! abstraction for this is the [PoolProvider] trait.
//!
//! It also contains the [StaticMemoryPool] as a concrete implementation which can be used to avoid
//! dynamic run-time allocations for the storage of TMTC packets.
//!
//! # Example for the [StaticMemoryPool]
//!
//! ```
//! use satrs::pool::{PoolProvider, StaticMemoryPool, StaticPoolConfig};
//! use satrs_core::pool::{PoolProvider, StaticMemoryPool, StaticPoolConfig};
//!
//! // 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes
//! let pool_cfg = StaticPoolConfig::new(vec![(4, 4), (2, 8), (1, 16)], false);
//! let pool_cfg = StaticPoolConfig::new(vec![(4, 4), (2, 8), (1, 16)]);
//! let mut local_pool = StaticMemoryPool::new(pool_cfg);
//! let mut read_buf: [u8; 16] = [0; 16];
//! let mut addr;
@ -210,12 +203,8 @@ impl Error for StoreError {
}
}
/// Generic trait for pool providers which provide memory pools for variable sized data.
///
/// It specifies a basic API to [Self::add], [Self::modify], [Self::read] and [Self::delete] data
/// in the store at its core. The API was designed so internal optimizations can be performed
/// more easily and that is is also possible to make the pool structure [Sync] without the whole
/// pool structure being wrapped inside a lock.
/// Generic trait for pool providers where the data can be modified and read in-place. This
/// generally means that a shared pool structure has to be wrapped inside a lock structure.
pub trait PoolProvider {
/// Add new data to the pool. The provider should attempt to reserve a memory block with the
/// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can
@ -262,7 +251,6 @@ pub trait PoolProvider {
}
}
/// Extension trait which adds guarded pool access classes.
pub trait PoolProviderWithGuards: PoolProvider {
/// This function behaves like [PoolProvider::read], but consumes the provided address
/// and returns a RAII conformant guard object.
@ -292,8 +280,7 @@ pub struct PoolGuard<'a, MemProvider: PoolProvider + ?Sized> {
deletion_failed_error: Option<StoreError>,
}
/// This helper object can be used to safely access pool data without worrying about memory
/// leaks.
/// This helper object
impl<'a, MemProvider: PoolProvider> PoolGuard<'a, MemProvider> {
pub fn new(pool: &'a mut MemProvider, addr: StoreAddr) -> Self {
Self {
@ -376,24 +363,16 @@ mod alloc_mod {
///
/// # Parameters
///
/// * `cfg` - Vector of tuples which represent a subpool. The first entry in the tuple specifies
/// the number of memory blocks in the subpool, the second entry the size of the blocks
/// * `spill_to_higher_subpools` - Specifies whether data will be spilled to higher subpools
/// if the next fitting subpool is full. This is useful to ensure the pool remains useful
/// for all data sizes as long as possible. However, an undesirable side-effect might be
/// the chocking of larger subpools by underdimensioned smaller subpools.
/// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the
/// number of memory blocks in the subpool, the second entry the size of the blocks
#[derive(Clone)]
pub struct StaticPoolConfig {
cfg: Vec<(NumBlocks, usize)>,
spill_to_higher_subpools: bool,
}
impl StaticPoolConfig {
pub fn new(cfg: Vec<(NumBlocks, usize)>, spill_to_higher_subpools: bool) -> Self {
StaticPoolConfig {
cfg,
spill_to_higher_subpools,
}
pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self {
StaticPoolConfig { cfg }
}
pub fn cfg(&self) -> &Vec<(NumBlocks, usize)> {
@ -411,21 +390,16 @@ mod alloc_mod {
/// Pool implementation providing sub-pools with fixed size memory blocks.
///
/// This is a simple memory pool implementation which pre-allocates all subpools using a given
/// pool configuration. After the pre-allocation, no dynamic memory allocation will be
/// performed during run-time. This makes the implementation suitable for real-time
/// applications and embedded environments.
///
/// The subpool bucket sizes only denote the maximum possible data size being stored inside
/// them and the pool implementation will still track the size of the data stored inside it.
/// The implementation will generally determine the best fitting subpool for given data to
/// add. Currently, the pool does not support spilling to larger subpools if the closest
/// fitting subpool is full. This might be added in the future.
/// This is a simple memory pool implementation which pre-allocates all sub-pools using a given pool
/// configuration. After the pre-allocation, no dynamic memory allocation will be performed
/// during run-time. This makes the implementation suitable for real-time applications and
/// embedded environments. The pool implementation will also track the size of the data stored
/// inside it.
///
/// Transactions with the [pool][StaticMemoryPool] are done using a generic
/// [address][StoreAddr] type. Adding any data to the pool will yield a store address.
/// Modification and read operations are done using a reference to a store address. Deletion
/// will consume the store address.
/// [address][StoreAddr] type.
/// Adding any data to the pool will yield a store address. Modification and read operations are
/// done using a reference to a store address. Deletion will consume the store address.
pub struct StaticMemoryPool {
pool_cfg: StaticPoolConfig,
pool: Vec<Vec<u8>>,
@ -482,17 +456,7 @@ mod alloc_mod {
}
fn reserve(&mut self, data_len: usize) -> Result<StaticPoolAddr, StoreError> {
let mut subpool_idx = self.find_subpool(data_len, 0)?;
if self.pool_cfg.spill_to_higher_subpools {
while let Err(StoreError::StoreFull(_)) = self.find_empty(subpool_idx) {
if (subpool_idx + 1) as usize == self.sizes_lists.len() {
return Err(StoreError::StoreFull(subpool_idx));
}
subpool_idx += 1;
}
}
let subpool_idx = self.find_subpool(data_len, 0)?;
let (slot, size_slot_ref) = self.find_empty(subpool_idx)?;
*size_slot_ref = data_len;
Ok(StaticPoolAddr {
@ -664,22 +628,22 @@ mod tests {
fn basic_small_pool() -> StaticMemoryPool {
// 4 buckets of 4 bytes, 2 of 8 bytes and 1 of 16 bytes
let pool_cfg = StaticPoolConfig::new(vec![(4, 4), (2, 8), (1, 16)], false);
let pool_cfg = StaticPoolConfig::new(vec![(4, 4), (2, 8), (1, 16)]);
StaticMemoryPool::new(pool_cfg)
}
#[test]
fn test_cfg() {
// Values where number of buckets is 0 or size is too large should be removed
let mut pool_cfg = StaticPoolConfig::new(vec![(0, 0), (1, 0), (2, POOL_MAX_SIZE)], false);
let mut pool_cfg = StaticPoolConfig::new(vec![(0, 0), (1, 0), (2, POOL_MAX_SIZE)]);
pool_cfg.sanitize();
assert_eq!(*pool_cfg.cfg(), vec![(1, 0)]);
// Entries should be ordered according to bucket size
pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)], false);
pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)]);
pool_cfg.sanitize();
assert_eq!(*pool_cfg.cfg(), vec![(32, 3), (16, 6), (8, 12)]);
// Unstable sort is used, so order of entries with same block length should not matter
pool_cfg = StaticPoolConfig::new(vec![(12, 12), (14, 16), (10, 12)], false);
pool_cfg = StaticPoolConfig::new(vec![(12, 12), (14, 16), (10, 12)]);
pool_cfg.sanitize();
assert!(
*pool_cfg.cfg() == vec![(12, 12), (10, 12), (14, 16)]
@ -972,72 +936,4 @@ mod tests {
})
.expect("Modifying data failed");
}
#[test]
fn test_spills_to_higher_subpools() {
let pool_cfg = StaticPoolConfig::new(vec![(2, 8), (2, 16)], true);
let mut local_pool = StaticMemoryPool::new(pool_cfg);
local_pool.free_element(8, |_| {}).unwrap();
local_pool.free_element(8, |_| {}).unwrap();
let mut in_larger_subpool_now = local_pool.free_element(8, |_| {});
assert!(in_larger_subpool_now.is_ok());
let generic_addr = in_larger_subpool_now.unwrap();
let pool_addr = StaticPoolAddr::from(generic_addr);
assert_eq!(pool_addr.pool_idx, 1);
assert_eq!(pool_addr.packet_idx, 0);
assert!(local_pool.has_element_at(&generic_addr).unwrap());
in_larger_subpool_now = local_pool.free_element(8, |_| {});
assert!(in_larger_subpool_now.is_ok());
let generic_addr = in_larger_subpool_now.unwrap();
let pool_addr = StaticPoolAddr::from(generic_addr);
assert_eq!(pool_addr.pool_idx, 1);
assert_eq!(pool_addr.packet_idx, 1);
assert!(local_pool.has_element_at(&generic_addr).unwrap());
}
#[test]
fn test_spillage_fails_as_well() {
let pool_cfg = StaticPoolConfig::new(vec![(1, 8), (1, 16)], true);
let mut local_pool = StaticMemoryPool::new(pool_cfg);
local_pool.free_element(8, |_| {}).unwrap();
local_pool.free_element(8, |_| {}).unwrap();
let should_fail = local_pool.free_element(8, |_| {});
assert!(should_fail.is_err());
if let Err(err) = should_fail {
assert_eq!(err, StoreError::StoreFull(1));
} else {
panic!("unexpected store address");
}
}
#[test]
fn test_spillage_works_across_multiple_subpools() {
let pool_cfg = StaticPoolConfig::new(vec![(1, 8), (1, 12), (1, 16)], true);
let mut local_pool = StaticMemoryPool::new(pool_cfg);
local_pool.free_element(8, |_| {}).unwrap();
local_pool.free_element(12, |_| {}).unwrap();
let in_larger_subpool_now = local_pool.free_element(8, |_| {});
assert!(in_larger_subpool_now.is_ok());
let generic_addr = in_larger_subpool_now.unwrap();
let pool_addr = StaticPoolAddr::from(generic_addr);
assert_eq!(pool_addr.pool_idx, 2);
assert_eq!(pool_addr.packet_idx, 0);
assert!(local_pool.has_element_at(&generic_addr).unwrap());
}
#[test]
fn test_spillage_fails_across_multiple_subpools() {
let pool_cfg = StaticPoolConfig::new(vec![(1, 8), (1, 12), (1, 16)], true);
let mut local_pool = StaticMemoryPool::new(pool_cfg);
local_pool.free_element(8, |_| {}).unwrap();
local_pool.free_element(12, |_| {}).unwrap();
local_pool.free_element(16, |_| {}).unwrap();
let should_fail = local_pool.free_element(8, |_| {});
assert!(should_fail.is_err());
if let Err(err) = should_fail {
assert_eq!(err, StoreError::StoreFull(2));
} else {
panic!("unexpected store address");
}
}
}

View File

@ -6,24 +6,16 @@ use spacepackets::ecss::event::Subservice;
use spacepackets::ecss::PusPacket;
use std::sync::mpsc::Sender;
use super::verification::VerificationReportingProvider;
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
pub struct PusService5EventHandler<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> {
pub service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
pub struct PusService5EventHandler<TcInMemConverter: EcssTcInMemConverter> {
pub service_helper: PusServiceHelper<TcInMemConverter>,
event_request_tx: Sender<EventRequestWithToken>,
}
impl<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> PusService5EventHandler<TcInMemConverter, VerificationReporter>
{
impl<TcInMemConverter: EcssTcInMemConverter> PusService5EventHandler<TcInMemConverter> {
pub fn new(
service_handler: PusServiceHelper<TcInMemConverter, VerificationReporter>,
service_handler: PusServiceHelper<TcInMemConverter>,
event_request_tx: Sender<EventRequestWithToken>,
) -> Self {
Self {
@ -52,10 +44,9 @@ impl<
}
let handle_enable_disable_request = |enable: bool, stamp: [u8; 7]| {
if tc.user_data().len() < 4 {
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 4,
found: tc.user_data().len(),
});
return Err(PusPacketHandlingError::NotEnoughAppData(
"at least 4 bytes event ID expected".into(),
));
}
let user_data = tc.user_data();
let event_u32 = EventU32::from(u32::from_be_bytes(user_data[0..4].try_into().unwrap()));
@ -63,7 +54,8 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &stamp)
.borrow_mut()
.start_success(ecss_tc_and_token.token, Some(&stamp))
.map_err(|_| PartialPusHandlingError::Verification);
let partial_error = start_token.clone().err();
let mut token: TcStateToken = ecss_tc_and_token.token.into();
@ -94,9 +86,7 @@ impl<
Ok(PusPacketHandlerResult::RequestHandled)
};
let mut partial_error = None;
let time_stamp = PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
&mut partial_error,
);
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
match srv.unwrap() {
Subservice::TmInfoReport
| Subservice::TmLowSeverityReport
@ -138,7 +128,7 @@ mod tests {
use crate::pus::event_man::EventRequest;
use crate::pus::tests::SimplePusPacketHandler;
use crate::pus::verification::{RequestId, VerificationReporterWithSender};
use crate::pus::verification::RequestId;
use crate::{
events::EventU32,
pus::{
@ -155,8 +145,7 @@ mod tests {
struct Pus5HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon,
handler:
PusService5EventHandler<EcssTcInSharedStoreConverter, VerificationReporterWithSender>,
handler: PusService5EventHandler<EcssTcInSharedStoreConverter>,
}
impl Pus5HandlerWithStoreTester {
@ -282,9 +271,8 @@ mod tests {
let result = test_harness.handle_one_tc();
assert!(result.is_err());
let result = result.unwrap_err();
if let PusPacketHandlingError::NotEnoughAppData { expected, found } = result {
assert_eq!(expected, 4);
assert_eq!(found, 3);
if let PusPacketHandlingError::NotEnoughAppData(string) = result {
assert_eq!(string, "at least 4 bytes event ID expected");
} else {
panic!("unexpected result type {result:?}")
}

1
satrs-core/src/pus/hk.rs Normal file
View File

@ -0,0 +1 @@
pub use spacepackets::ecss::hk::*;

View File

@ -2,7 +2,6 @@
//!
//! This module contains structures to make working with the PUS C standard easier.
//! The satrs-example application contains various usage examples of these components.
use crate::queue::{GenericRecvError, GenericSendError};
use crate::ChannelId;
use core::fmt::{Display, Formatter};
#[cfg(feature = "alloc")]
@ -17,7 +16,6 @@ use spacepackets::ecss::tm::PusTmCreator;
use spacepackets::ecss::PusError;
use spacepackets::{ByteConversionError, SpHeader};
pub mod action;
pub mod event;
pub mod event_man;
#[cfg(feature = "std")]
@ -57,6 +55,52 @@ impl<'tm> From<PusTmCreator<'tm>> for PusTmWrapper<'tm> {
}
}
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone)]
pub enum GenericSendError {
RxDisconnected,
QueueFull(Option<u32>),
}
impl Display for GenericSendError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
GenericSendError::RxDisconnected => {
write!(f, "rx side has disconnected")
}
GenericSendError::QueueFull(max_cap) => {
write!(f, "queue with max capacity of {max_cap:?} is full")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericSendError {}
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone)]
pub enum GenericRecvError {
Empty,
TxDisconnected,
}
impl Display for GenericRecvError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::TxDisconnected => {
write!(f, "tx side has disconnected")
}
Self::Empty => {
write!(f, "nothing to receive")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericRecvError {}
#[derive(Debug, Clone)]
pub enum EcssTmtcError {
StoreLock,
@ -260,12 +304,8 @@ pub trait ReceivesEcssPusTc {
#[cfg(feature = "alloc")]
mod alloc_mod {
use crate::TargetId;
use super::*;
use crate::pus::verification::VerificationReportingProvider;
/// Extension trait for [EcssTmSenderCore].
///
/// It provides additional functionality, for example by implementing the [Downcast] trait
@ -345,33 +385,22 @@ mod alloc_mod {
impl<T> EcssTcReceiver for T where T: EcssTcReceiverCore + 'static {}
impl_downcast!(EcssTcReceiver);
pub trait PusRoutingErrorHandler {
type Error;
fn handle_error(
&self,
target_id: TargetId,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
error: Self::Error,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
);
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std_mod {
use crate::pool::{PoolProvider, PoolProviderWithGuards, SharedStaticMemoryPool, StoreAddr};
use crate::pus::verification::{TcStateAccepted, VerificationToken};
use crate::pus::verification::{
StdVerifReporterWithSender, TcStateAccepted, VerificationToken,
};
use crate::pus::{
EcssChannel, EcssTcAndToken, EcssTcReceiver, EcssTcReceiverCore, EcssTmSender,
EcssTmSenderCore, EcssTmtcError, GenericRecvError, GenericSendError, PusTmWrapper,
TryRecvTmtcError,
};
use crate::tmtc::tm_helper::SharedTmPool;
use crate::{ChannelId, TargetId};
use crate::ChannelId;
use alloc::boxed::Box;
use alloc::vec::Vec;
use crossbeam_channel as cb;
@ -381,12 +410,13 @@ pub mod std_mod {
use spacepackets::time::cds::TimeProvider;
use spacepackets::time::StdTimestampError;
use spacepackets::time::TimeWriter;
use std::cell::RefCell;
use std::string::String;
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;
use thiserror::Error;
use super::verification::VerificationReportingProvider;
use super::verification::VerificationReporterWithSender;
use super::{AcceptedEcssTcAndToken, TcInMemory};
impl From<mpsc::SendError<StoreAddr>> for EcssTmtcError {
@ -632,20 +662,6 @@ pub mod std_mod {
}
}
// TODO: All these types could probably be no_std if we implemented error handling ourselves..
// but thiserror is really nice, so keep it like this for simplicity for now. Maybe thiserror
// will be no_std soon, see https://github.com/rust-lang/rust/issues/103765 .
#[derive(Debug, Clone, Error)]
pub enum GenericRoutingError {
#[error("not enough application data, expected at least {expected}, found {found}")]
NotEnoughAppData { expected: usize, found: usize },
#[error("Unknown target ID {0}")]
UnknownTargetId(TargetId),
#[error("Sending action request failed: {0}")]
SendError(GenericSendError),
}
#[derive(Debug, Clone, Error)]
pub enum PusPacketHandlingError {
#[error("generic PUS error: {0}")]
@ -654,8 +670,8 @@ pub mod std_mod {
WrongService(u8),
#[error("invalid subservice {0}")]
InvalidSubservice(u8),
#[error("not enough application data, expected at least {expected}, found {found}")]
NotEnoughAppData { expected: usize, found: usize },
#[error("not enough application data available: {0}")]
NotEnoughAppData(String),
#[error("PUS packet too large, does not fit in buffer: {0}")]
PusPacketTooLarge(usize),
#[error("invalid application data")]
@ -666,8 +682,6 @@ pub mod std_mod {
EcssTmtc(#[from] EcssTmtcError),
#[error("invalid verification token")]
InvalidVerificationToken,
#[error("request routing error: {0}")]
RequestRoutingError(#[from] GenericRoutingError),
#[error("other error {0}")]
Other(String),
}
@ -811,18 +825,18 @@ pub mod std_mod {
}
}
pub struct PusServiceBase<VerificationReporter: VerificationReportingProvider> {
pub struct PusServiceBase {
pub tc_receiver: Box<dyn EcssTcReceiver>,
pub tm_sender: Box<dyn EcssTmSender>,
pub tm_apid: u16,
/// The verification handler is wrapped in a [RefCell] to allow the interior mutability
/// pattern. This makes writing methods which are not mutable a lot easier.
pub verification_handler: VerificationReporter,
pub verification_handler: RefCell<StdVerifReporterWithSender>,
}
impl<VerificationReporter: VerificationReportingProvider> PusServiceBase<VerificationReporter> {
impl PusServiceBase {
#[cfg(feature = "std")]
pub fn get_current_cds_short_timestamp(
pub fn get_current_timestamp(
partial_error: &mut Option<PartialPusHandlingError>,
) -> [u8; 7] {
let mut time_stamp: [u8; 7] = [0; 7];
@ -836,10 +850,11 @@ pub mod std_mod {
}
time_stamp
}
#[cfg(feature = "std")]
pub fn get_current_timestamp_ignore_error() -> [u8; 7] {
let mut dummy = None;
Self::get_current_cds_short_timestamp(&mut dummy)
Self::get_current_timestamp(&mut dummy)
}
}
@ -852,24 +867,17 @@ pub mod std_mod {
/// This base class can handle PUS telecommands backed by different memory storage machanisms
/// by using the [EcssTcInMemConverter] abstraction. This object provides some convenience
/// methods to make the generic parts of TC handling easier.
pub struct PusServiceHelper<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> {
pub common: PusServiceBase<VerificationReporter>,
pub struct PusServiceHelper<TcInMemConverter: EcssTcInMemConverter> {
pub common: PusServiceBase,
pub tc_in_mem_converter: TcInMemConverter,
}
impl<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> PusServiceHelper<TcInMemConverter, VerificationReporter>
{
impl<TcInMemConverter: EcssTcInMemConverter> PusServiceHelper<TcInMemConverter> {
pub fn new(
tc_receiver: Box<dyn EcssTcReceiver>,
tm_sender: Box<dyn EcssTmSender>,
tm_apid: u16,
verification_handler: VerificationReporter,
verification_handler: VerificationReporterWithSender,
tc_in_mem_converter: TcInMemConverter,
) -> Self {
Self {
@ -877,7 +885,7 @@ pub mod std_mod {
tc_receiver,
tm_sender,
tm_apid,
verification_handler,
verification_handler: RefCell::new(verification_handler),
},
tc_in_mem_converter,
}
@ -931,15 +939,12 @@ pub(crate) fn source_buffer_large_enough(cap: usize, len: usize) -> Result<(), E
#[cfg(test)]
pub mod tests {
use core::cell::RefCell;
use std::sync::mpsc::TryRecvError;
use std::sync::{mpsc, RwLock};
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use satrs_shared::res_code::ResultU16;
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
use alloc::vec;
use spacepackets::ecss::tc::PusTcCreator;
use spacepackets::ecss::tm::{GenericPusTmSecondaryHeader, PusTmCreator, PusTmReader};
use spacepackets::ecss::{PusPacket, WritablePusPacket};
use spacepackets::CcsdsPacket;
@ -949,17 +954,14 @@ pub mod tests {
};
use crate::pus::verification::RequestId;
use crate::tmtc::tm_helper::SharedTmPool;
use crate::TargetId;
use super::verification::tests::{SharedVerificationMap, TestVerificationReporter};
use super::verification::{
TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender,
VerificationReportingProvider, VerificationToken,
TcStateAccepted, VerificationReporterCfg, VerificationReporterWithSender, VerificationToken,
};
use super::{
EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, GenericRoutingError,
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
PusPacketHandlingError, PusRoutingErrorHandler, PusServiceHelper, TcInMemory,
EcssTcAndToken, EcssTcInSharedStoreConverter, EcssTcInVecConverter, MpscTcReceiver,
MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
PusPacketHandlingError, PusServiceHelper, TcInMemory,
};
pub const TEST_APID: u16 = 0x101;
@ -1014,11 +1016,8 @@ pub mod tests {
/// [PusServiceHandler] which might be required for a specific PUS service handler.
///
/// The PUS service handler is instantiated with a [EcssTcInStoreConverter].
pub fn new() -> (
Self,
PusServiceHelper<EcssTcInSharedStoreConverter, VerificationReporterWithSender>,
) {
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
pub fn new() -> (Self, PusServiceHelper<EcssTcInSharedStoreConverter>) {
let pool_cfg = StaticPoolConfig::new(vec![(16, 16), (8, 32), (4, 64)]);
let tc_pool = StaticMemoryPool::new(pool_cfg.clone());
let tm_pool = StaticMemoryPool::new(pool_cfg);
let shared_tc_pool = SharedStaticMemoryPool::new(RwLock::new(tc_pool));
@ -1063,7 +1062,7 @@ pub mod tests {
let token = self.verification_handler.add_tc(tc);
let token = self
.verification_handler
.acceptance_success(token, &[0; 7])
.acceptance_success(token, Some(&[0; 7]))
.unwrap();
let tc_size = tc.write_to_bytes(&mut self.pus_buf).unwrap();
let mut tc_pool = self.tc_pool.write().unwrap();
@ -1110,18 +1109,15 @@ pub mod tests {
}
}
pub struct PusServiceHandlerWithVecCommon<VerificationReporter: VerificationReportingProvider> {
pub struct PusServiceHandlerWithVecCommon {
current_tm: Option<alloc::vec::Vec<u8>>,
tc_sender: mpsc::Sender<EcssTcAndToken>,
tm_receiver: mpsc::Receiver<alloc::vec::Vec<u8>>,
pub verification_handler: VerificationReporter,
verification_handler: VerificationReporterWithSender,
}
impl PusServiceHandlerWithVecCommon<VerificationReporterWithSender> {
pub fn new_with_standard_verif_reporter() -> (
Self,
PusServiceHelper<EcssTcInVecConverter, VerificationReporterWithSender>,
) {
impl PusServiceHandlerWithVecCommon {
pub fn new() -> (Self, PusServiceHelper<EcssTcInVecConverter>) {
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
let (tm_tx, tm_rx) = mpsc::channel();
@ -1129,7 +1125,6 @@ pub mod tests {
let verif_cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
let verification_handler =
VerificationReporterWithSender::new(&verif_cfg, Box::new(verif_sender));
let test_srv_tm_sender = MpscTmAsVecSender::new(0, "test-sender", tm_tx);
let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx);
let in_store_converter = EcssTcInVecConverter::default();
@ -1149,47 +1144,12 @@ pub mod tests {
),
)
}
}
impl PusServiceHandlerWithVecCommon<TestVerificationReporter> {
pub fn new_with_test_verif_sender() -> (
Self,
PusServiceHelper<EcssTcInVecConverter, TestVerificationReporter>,
) {
let (test_srv_tc_tx, test_srv_tc_rx) = mpsc::channel();
let (tm_tx, tm_rx) = mpsc::channel();
let test_srv_tm_sender = MpscTmAsVecSender::new(0, "test-sender", tm_tx);
let test_srv_tc_receiver = MpscTcReceiver::new(0, "test-receiver", test_srv_tc_rx);
let in_store_converter = EcssTcInVecConverter::default();
let shared_verif_map = SharedVerificationMap::default();
let verification_handler = TestVerificationReporter::new(shared_verif_map);
(
Self {
current_tm: None,
tc_sender: test_srv_tc_tx,
tm_receiver: tm_rx,
verification_handler: verification_handler.clone(),
},
PusServiceHelper::new(
Box::new(test_srv_tc_receiver),
Box::new(test_srv_tm_sender),
TEST_APID,
verification_handler,
in_store_converter,
),
)
}
}
impl<VerificationReporter: VerificationReportingProvider>
PusServiceHandlerWithVecCommon<VerificationReporter>
{
pub fn send_tc(&mut self, tc: &PusTcCreator) -> VerificationToken<TcStateAccepted> {
let token = self.verification_handler.add_tc(tc);
let token = self
.verification_handler
.acceptance_success(token, &[0; 7])
.acceptance_success(token, Some(&[0; 7]))
.unwrap();
// Send accepted TC to test service handler.
self.tc_sender
@ -1231,106 +1191,4 @@ pub mod tests {
assert_eq!(req_id, expected_request_id);
}
}
pub const APP_DATA_TOO_SHORT: ResultU16 = ResultU16::new(1, 1);
#[derive(Default)]
pub struct TestConverter<const SERVICE: u8> {
pub conversion_request: VecDeque<Vec<u8>>,
}
impl<const SERVICE: u8> TestConverter<SERVICE> {
pub fn check_service(&self, tc: &PusTcReader) -> Result<(), PusPacketHandlingError> {
if tc.service() != SERVICE {
return Err(PusPacketHandlingError::WrongService(tc.service()));
}
Ok(())
}
pub fn is_empty(&self) {
self.conversion_request.is_empty();
}
pub fn check_next_conversion(&mut self, tc: &PusTcCreator) {
assert!(!self.conversion_request.is_empty());
assert_eq!(
self.conversion_request.pop_front().unwrap(),
tc.to_vec().unwrap()
);
}
}
#[derive(Default)]
pub struct TestRoutingErrorHandler {
pub routing_errors: RefCell<VecDeque<(TargetId, GenericRoutingError)>>,
}
impl PusRoutingErrorHandler for TestRoutingErrorHandler {
type Error = GenericRoutingError;
fn handle_error(
&self,
target_id: TargetId,
_token: VerificationToken<TcStateAccepted>,
_tc: &PusTcReader,
error: Self::Error,
_time_stamp: &[u8],
_verif_reporter: &impl VerificationReportingProvider,
) {
self.routing_errors
.borrow_mut()
.push_back((target_id, error));
}
}
impl TestRoutingErrorHandler {
pub fn is_empty(&self) -> bool {
self.routing_errors.borrow().is_empty()
}
pub fn retrieve_next_error(&mut self) -> (TargetId, GenericRoutingError) {
if self.routing_errors.borrow().is_empty() {
panic!("no routing request available");
}
self.routing_errors.borrow_mut().pop_front().unwrap()
}
}
pub struct TestRouter<REQUEST> {
pub routing_requests: RefCell<VecDeque<(TargetId, REQUEST)>>,
pub injected_routing_failure: RefCell<Option<GenericRoutingError>>,
}
impl<REQUEST> Default for TestRouter<REQUEST> {
fn default() -> Self {
Self {
routing_requests: Default::default(),
injected_routing_failure: Default::default(),
}
}
}
impl<REQUEST> TestRouter<REQUEST> {
pub fn check_for_injected_error(&self) -> Result<(), GenericRoutingError> {
if self.injected_routing_failure.borrow().is_some() {
return Err(self.injected_routing_failure.borrow_mut().take().unwrap());
}
Ok(())
}
pub fn inject_routing_error(&mut self, error: GenericRoutingError) {
*self.injected_routing_failure.borrow_mut() = Some(error);
}
pub fn is_empty(&self) -> bool {
self.routing_requests.borrow().is_empty()
}
pub fn retrieve_next_request(&mut self) -> (TargetId, REQUEST) {
if self.routing_requests.borrow().is_empty() {
panic!("no routing request available");
}
self.routing_requests.borrow_mut().pop_front().unwrap()
}
}
}

View File

@ -238,8 +238,7 @@ impl Error for ScheduleError {
}
}
/// Generic trait for scheduler objects which are able to schedule ECSS PUS C packets.
pub trait PusSchedulerProvider {
pub trait PusSchedulerInterface {
type TimeProvider: CcsdsTimeProvider + TimeReader;
fn reset(&mut self, store: &mut (impl PoolProvider + ?Sized)) -> Result<(), StoreError>;
@ -783,7 +782,7 @@ pub mod alloc_mod {
}
}
impl PusSchedulerProvider for PusScheduler {
impl PusSchedulerInterface for PusScheduler {
type TimeProvider = cds::TimeProvider;
/// This will disable the scheduler and clear the schedule as specified in 6.11.4.4.
@ -946,7 +945,7 @@ mod tests {
#[test]
fn test_reset() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
@ -1098,7 +1097,7 @@ mod tests {
}
#[test]
fn test_release_telecommands() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
@ -1164,7 +1163,7 @@ mod tests {
#[test]
fn release_multi_with_same_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
@ -1222,7 +1221,7 @@ mod tests {
#[test]
fn release_with_scheduler_disabled() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
@ -1292,7 +1291,7 @@ mod tests {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut buf: [u8; 32] = [0; 32];
let tc_info_0 = ping_tc_to_store(&mut pool, &mut buf, 0, None);
@ -1340,7 +1339,7 @@ mod tests {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut buf: [u8; 32] = [0; 32];
let tc = scheduled_tc(UnixTimestamp::new_only_seconds(100), &mut buf);
@ -1390,7 +1389,7 @@ mod tests {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut buf: [u8; 32] = [0; 32];
let tc = wrong_tc_service(UnixTimestamp::new_only_seconds(100), &mut buf);
@ -1413,7 +1412,7 @@ mod tests {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut buf: [u8; 32] = [0; 32];
let tc = wrong_tc_subservice(UnixTimestamp::new_only_seconds(100), &mut buf);
@ -1435,7 +1434,7 @@ mod tests {
fn insert_wrapped_tc_faulty_app_data() {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let tc = invalid_time_tagged_cmd();
let insert_res = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
assert!(insert_res.is_err());
@ -1450,7 +1449,7 @@ mod tests {
fn insert_doubly_wrapped_time_tagged_cmd() {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut buf: [u8; 64] = [0; 64];
let tc = double_wrapped_time_tagged_tc(UnixTimestamp::new_only_seconds(50), &mut buf);
let insert_res = scheduler.insert_wrapped_tc::<cds::TimeProvider>(&tc, &mut pool);
@ -1486,7 +1485,7 @@ mod tests {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut buf: [u8; 32] = [0; 32];
@ -1510,7 +1509,7 @@ mod tests {
#[test]
fn test_store_error_propagation_release() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
@ -1545,7 +1544,7 @@ mod tests {
#[test]
fn test_store_error_propagation_reset() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
@ -1569,7 +1568,7 @@ mod tests {
#[test]
fn test_delete_by_req_id_simple_retrieve_addr() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
@ -1588,7 +1587,7 @@ mod tests {
#[test]
fn test_delete_by_req_id_simple_delete_all() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
@ -1607,7 +1606,7 @@ mod tests {
#[test]
fn test_delete_by_req_id_complex() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut buf: [u8; 32] = [0; 32];
@ -1654,7 +1653,7 @@ mod tests {
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(1, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(1, 64)]));
let mut buf: [u8; 32] = [0; 32];
// Store is full after this.
@ -1693,7 +1692,7 @@ mod tests {
#[test]
fn test_time_window_retrieval_select_all() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1724,7 +1723,7 @@ mod tests {
#[test]
fn test_time_window_retrieval_select_from_stamp() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1755,7 +1754,7 @@ mod tests {
#[test]
fn test_time_window_retrieval_select_to_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let tc_info_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1786,7 +1785,7 @@ mod tests {
#[test]
fn test_time_window_retrieval_select_from_time_to_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let _ = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1821,7 +1820,7 @@ mod tests {
#[test]
fn test_deletion_all() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1848,7 +1847,7 @@ mod tests {
#[test]
fn test_deletion_from_start_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1869,7 +1868,7 @@ mod tests {
#[test]
fn test_deletion_to_end_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let cmd_0_to_delete = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1891,7 +1890,7 @@ mod tests {
#[test]
fn test_deletion_from_start_time_to_end_time() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));
let cmd_out_of_range_0 = insert_command_with_release_time(&mut pool, &mut scheduler, 0, 50);
@ -1920,7 +1919,7 @@ mod tests {
#[test]
fn test_release_without_deletion() {
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)], false));
let mut pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(10, 32), (5, 64)]));
let mut scheduler =
PusScheduler::new(UnixTimestamp::new_only_seconds(0), Duration::from_secs(5));

View File

@ -1,5 +1,4 @@
use super::scheduler::PusSchedulerProvider;
use super::verification::VerificationReportingProvider;
use super::scheduler::PusSchedulerInterface;
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
use crate::pool::PoolProvider;
use crate::pus::{PusPacketHandlerResult, PusPacketHandlingError};
@ -9,7 +8,7 @@ use spacepackets::time::cds::TimeProvider;
/// This is a helper class for [std] environments to handle generic PUS 11 (scheduling service)
/// packets. This handler is able to handle the most important PUS requests for a scheduling
/// service which provides the [PusSchedulerProvider].
/// service which provides the [PusSchedulerInterface].
///
/// Please note that this class does not do the regular periodic handling like releasing any
/// telecommands inside the scheduler. The user can retrieve the wrapped scheduler via the
@ -17,23 +16,16 @@ use spacepackets::time::cds::TimeProvider;
/// telecommands when applicable.
pub struct PusService11SchedHandler<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
PusScheduler: PusSchedulerProvider,
Scheduler: PusSchedulerInterface,
> {
pub service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
scheduler: PusScheduler,
pub service_helper: PusServiceHelper<TcInMemConverter>,
scheduler: Scheduler,
}
impl<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
Scheduler: PusSchedulerProvider,
> PusService11SchedHandler<TcInMemConverter, VerificationReporter, Scheduler>
impl<TcInMemConverter: EcssTcInMemConverter, Scheduler: PusSchedulerInterface>
PusService11SchedHandler<TcInMemConverter, Scheduler>
{
pub fn new(
service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
scheduler: Scheduler,
) -> Self {
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>, scheduler: Scheduler) -> Self {
Self {
service_helper,
scheduler,
@ -70,16 +62,15 @@ impl<
));
}
let mut partial_error = None;
let time_stamp = PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
&mut partial_error,
);
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
match standard_subservice.unwrap() {
scheduling::Subservice::TcEnableScheduling => {
let start_token = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.get_mut()
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
.expect("Error sending start success");
self.scheduler.enable();
@ -87,7 +78,8 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.get_mut()
.completion_success(start_token, Some(&time_stamp))
.expect("Error sending completion success");
} else {
return Err(PusPacketHandlingError::Other(
@ -100,7 +92,8 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.get_mut()
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
.expect("Error sending start success");
self.scheduler.disable();
@ -108,7 +101,8 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.get_mut()
.completion_success(start_token, Some(&time_stamp))
.expect("Error sending completion success");
} else {
return Err(PusPacketHandlingError::Other(
@ -121,7 +115,8 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.get_mut()
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
.expect("Error sending start success");
self.scheduler
@ -131,7 +126,8 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.get_mut()
.completion_success(start_token, Some(&time_stamp))
.expect("Error sending completion success");
}
scheduling::Subservice::TcInsertActivity => {
@ -139,7 +135,8 @@ impl<
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.get_mut()
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
.expect("error sending start success");
// let mut pool = self.sched_tc_pool.write().expect("locking pool failed");
@ -150,7 +147,8 @@ impl<
self.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.get_mut()
.completion_success(start_token, Some(&time_stamp))
.expect("sending completion success failed");
}
_ => {
@ -174,9 +172,8 @@ impl<
mod tests {
use crate::pool::{StaticMemoryPool, StaticPoolConfig};
use crate::pus::tests::TEST_APID;
use crate::pus::verification::VerificationReporterWithSender;
use crate::pus::{
scheduler::{self, PusSchedulerProvider, TcInfo},
scheduler::{self, PusSchedulerInterface, TcInfo},
tests::{PusServiceHandlerWithSharedStoreCommon, PusTestHarness},
verification::{RequestId, TcStateAccepted, VerificationToken},
EcssTcInSharedStoreConverter,
@ -197,18 +194,14 @@ mod tests {
struct Pus11HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon,
handler: PusService11SchedHandler<
EcssTcInSharedStoreConverter,
VerificationReporterWithSender,
TestScheduler,
>,
handler: PusService11SchedHandler<EcssTcInSharedStoreConverter, TestScheduler>,
sched_tc_pool: StaticMemoryPool,
}
impl Pus11HandlerWithStoreTester {
pub fn new() -> Self {
let test_scheduler = TestScheduler::default();
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)], false);
let pool_cfg = StaticPoolConfig::new(alloc::vec![(16, 16), (8, 32), (4, 64)]);
let sched_tc_pool = StaticMemoryPool::new(pool_cfg.clone());
let (common, srv_handler) = PusServiceHandlerWithSharedStoreCommon::new();
Self {
@ -239,7 +232,7 @@ mod tests {
inserted_tcs: VecDeque<TcInfo>,
}
impl PusSchedulerProvider for TestScheduler {
impl PusSchedulerInterface for TestScheduler {
type TimeProvider = cds::TimeProvider;
fn reset(

View File

@ -5,24 +5,16 @@ use spacepackets::ecss::tm::{PusTmCreator, PusTmSecondaryHeader};
use spacepackets::ecss::PusPacket;
use spacepackets::SpHeader;
use super::verification::VerificationReportingProvider;
use super::{EcssTcInMemConverter, PusServiceBase, PusServiceHelper};
/// This is a helper class for [std] environments to handle generic PUS 17 (test service) packets.
/// This handler only processes ping requests and generates a ping reply for them accordingly.
pub struct PusService17TestHandler<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> {
pub service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>,
pub struct PusService17TestHandler<TcInMemConverter: EcssTcInMemConverter> {
pub service_helper: PusServiceHelper<TcInMemConverter>,
}
impl<
TcInMemConverter: EcssTcInMemConverter,
VerificationReporter: VerificationReportingProvider,
> PusService17TestHandler<TcInMemConverter, VerificationReporter>
{
pub fn new(service_helper: PusServiceHelper<TcInMemConverter, VerificationReporter>) -> Self {
impl<TcInMemConverter: EcssTcInMemConverter> PusService17TestHandler<TcInMemConverter> {
pub fn new(service_helper: PusServiceHelper<TcInMemConverter>) -> Self {
Self { service_helper }
}
@ -41,15 +33,13 @@ impl<
}
if tc.subservice() == 1 {
let mut partial_error = None;
let time_stamp =
PusServiceBase::<VerificationReporter>::get_current_cds_short_timestamp(
&mut partial_error,
);
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
let result = self
.service_helper
.common
.verification_handler
.start_success(ecss_tc_and_token.token, &time_stamp)
.get_mut()
.start_success(ecss_tc_and_token.token, Some(&time_stamp))
.map_err(|_| PartialPusHandlingError::Verification);
let start_token = if let Ok(result) = result {
Some(result)
@ -77,7 +67,8 @@ impl<
.service_helper
.common
.verification_handler
.completion_success(start_token, &time_stamp)
.get_mut()
.completion_success(start_token, Some(&time_stamp))
.is_err()
{
partial_error = Some(PartialPusHandlingError::Verification)
@ -104,7 +95,7 @@ mod tests {
PusServiceHandlerWithSharedStoreCommon, PusServiceHandlerWithVecCommon, PusTestHarness,
SimplePusPacketHandler, TEST_APID,
};
use crate::pus::verification::{RequestId, VerificationReporterWithSender};
use crate::pus::verification::RequestId;
use crate::pus::verification::{TcStateAccepted, VerificationToken};
use crate::pus::{
EcssTcInSharedStoreConverter, EcssTcInVecConverter, PusPacketHandlerResult,
@ -120,8 +111,7 @@ mod tests {
struct Pus17HandlerWithStoreTester {
common: PusServiceHandlerWithSharedStoreCommon,
handler:
PusService17TestHandler<EcssTcInSharedStoreConverter, VerificationReporterWithSender>,
handler: PusService17TestHandler<EcssTcInSharedStoreConverter>,
}
impl Pus17HandlerWithStoreTester {
@ -158,14 +148,13 @@ mod tests {
}
struct Pus17HandlerWithVecTester {
common: PusServiceHandlerWithVecCommon<VerificationReporterWithSender>,
handler: PusService17TestHandler<EcssTcInVecConverter, VerificationReporterWithSender>,
common: PusServiceHandlerWithVecCommon,
handler: PusService17TestHandler<EcssTcInVecConverter>,
}
impl Pus17HandlerWithVecTester {
pub fn new() -> Self {
let (common, srv_handler) =
PusServiceHandlerWithVecCommon::new_with_standard_verif_reporter();
let (common, srv_handler) = PusServiceHandlerWithVecCommon::new();
Self {
common,
handler: PusService17TestHandler::new(srv_handler),

View File

@ -15,13 +15,11 @@
//! ```
//! use std::sync::{Arc, mpsc, RwLock};
//! use std::time::Duration;
//! use satrs::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
//! use satrs::pus::verification::{
//! VerificationReportingProvider, VerificationReporterCfg, VerificationReporterWithSender
//! };
//! use satrs::seq_count::SeqCountProviderSimple;
//! use satrs::pus::MpscTmInSharedPoolSender;
//! use satrs::tmtc::tm_helper::SharedTmPool;
//! use satrs_core::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
//! use satrs_core::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
//! use satrs_core::seq_count::SeqCountProviderSimple;
//! use satrs_core::pus::MpscTmInSharedPoolSender;
//! use satrs_core::tmtc::tm_helper::SharedTmPool;
//! use spacepackets::ecss::PusPacket;
//! use spacepackets::SpHeader;
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
@ -30,7 +28,7 @@
//! const EMPTY_STAMP: [u8; 7] = [0; 7];
//! const TEST_APID: u16 = 0x02;
//!
//! let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false);
//! let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
//! let tm_pool = StaticMemoryPool::new(pool_cfg.clone());
//! let shared_tm_store = SharedTmPool::new(tm_pool);
//! let tm_store = shared_tm_store.clone_backing_pool();
@ -45,9 +43,9 @@
//! let init_token = reporter.add_tc(&pus_tc_0);
//!
//! // Complete success sequence for a telecommand
//! let accepted_token = reporter.acceptance_success(init_token, &EMPTY_STAMP).unwrap();
//! let started_token = reporter.start_success(accepted_token, &EMPTY_STAMP).unwrap();
//! reporter.completion_success(started_token, &EMPTY_STAMP).unwrap();
//! let accepted_token = reporter.acceptance_success(init_token, Some(&EMPTY_STAMP)).unwrap();
//! let started_token = reporter.start_success(accepted_token, Some(&EMPTY_STAMP)).unwrap();
//! reporter.completion_success(started_token, Some(&EMPTY_STAMP)).unwrap();
//!
//! // Verify it arrives correctly on receiver end
//! let mut tm_buf: [u8; 1024] = [0; 1024];
@ -281,16 +279,16 @@ impl<STATE> VerificationToken<STATE> {
/// Composite helper struct to pass failure parameters to the [VerificationReporter]
pub struct FailParams<'stamp, 'fargs> {
time_stamp: &'stamp [u8],
time_stamp: Option<&'stamp [u8]>,
failure_code: &'fargs dyn EcssEnumeration,
failure_data: &'fargs [u8],
failure_data: Option<&'fargs [u8]>,
}
impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
pub fn new(
time_stamp: &'stamp [u8],
time_stamp: Option<&'stamp [u8]>,
failure_code: &'fargs impl EcssEnumeration,
failure_data: &'fargs [u8],
failure_data: Option<&'fargs [u8]>,
) -> Self {
Self {
time_stamp,
@ -298,13 +296,6 @@ impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
failure_data,
}
}
pub fn new_no_fail_data(
time_stamp: &'stamp [u8],
failure_code: &'fargs impl EcssEnumeration,
) -> Self {
Self::new(time_stamp, failure_code, &[])
}
}
/// Composite helper struct to pass step failure parameters to the [VerificationReporter]
@ -315,10 +306,10 @@ pub struct FailParamsWithStep<'stamp, 'fargs> {
impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> {
pub fn new(
time_stamp: &'stamp [u8],
time_stamp: Option<&'stamp [u8]>,
step: &'fargs impl EcssEnumeration,
failure_code: &'fargs impl EcssEnumeration,
failure_data: &'fargs [u8],
failure_data: Option<&'fargs [u8]>,
) -> Self {
Self {
bp: FailParams::new(time_stamp, failure_code, failure_data),
@ -408,66 +399,6 @@ impl<'src_data, TcState: WasAtLeastAccepted + Copy>
pub fn send_success_step_or_completion_success(self) {}
}
pub trait VerificationReportingProvider {
fn add_tc(
&mut self,
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
) -> VerificationToken<TcStateNone> {
self.add_tc_with_req_id(RequestId::new(pus_tc))
}
fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
fn acceptance_success(
&self,
token: VerificationToken<TcStateNone>,
time_stamp: &[u8],
) -> Result<VerificationToken<TcStateAccepted>, VerificationOrSendErrorWithToken<TcStateNone>>;
fn acceptance_failure(
&self,
token: VerificationToken<TcStateNone>,
params: FailParams,
) -> Result<(), VerificationOrSendErrorWithToken<TcStateNone>>;
fn start_success(
&self,
token: VerificationToken<TcStateAccepted>,
time_stamp: &[u8],
) -> Result<VerificationToken<TcStateStarted>, VerificationOrSendErrorWithToken<TcStateAccepted>>;
fn start_failure(
&self,
token: VerificationToken<TcStateAccepted>,
params: FailParams,
) -> Result<(), VerificationOrSendErrorWithToken<TcStateAccepted>>;
fn step_success(
&self,
token: &VerificationToken<TcStateStarted>,
time_stamp: &[u8],
step: impl EcssEnumeration,
) -> Result<(), EcssTmtcError>;
fn step_failure(
&self,
token: VerificationToken<TcStateStarted>,
params: FailParamsWithStep,
) -> Result<(), VerificationOrSendErrorWithToken<TcStateStarted>>;
fn completion_success<TcState: WasAtLeastAccepted + Copy>(
&self,
token: VerificationToken<TcState>,
time_stamp: &[u8],
) -> Result<(), VerificationOrSendErrorWithToken<TcState>>;
fn completion_failure<TcState: WasAtLeastAccepted + Copy>(
&self,
token: VerificationToken<TcState>,
params: FailParams,
) -> Result<(), VerificationOrSendErrorWithToken<TcState>>;
}
/// Primary verification handler. It provides an API to send PUS 1 verification telemetry packets
/// and verify the various steps of telecommand handling as specified in the PUS standard.
///
@ -525,7 +456,7 @@ impl VerificationReporterCore {
token: VerificationToken<State>,
seq_count: u16,
msg_count: u16,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
) -> Result<
VerificationSendable<'src_data, State, VerifSuccess>,
VerificationErrorWithToken<State>,
@ -582,7 +513,7 @@ impl VerificationReporterCore {
token: VerificationToken<TcStateNone>,
seq_count: u16,
msg_count: u16,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
) -> Result<
VerificationSendable<'src_data, TcStateNone, VerifSuccess>,
VerificationErrorWithToken<TcStateNone>,
@ -653,7 +584,7 @@ impl VerificationReporterCore {
token: VerificationToken<TcStateAccepted>,
seq_count: u16,
msg_count: u16,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
) -> Result<
VerificationSendable<'src_data, TcStateAccepted, VerifSuccess>,
VerificationErrorWithToken<TcStateAccepted>,
@ -727,7 +658,7 @@ impl VerificationReporterCore {
token: &VerificationToken<TcStateStarted>,
seq_count: u16,
msg_count: u16,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
step: impl EcssEnumeration,
) -> Result<VerificationSendable<'src_data, TcStateStarted, VerifSuccess>, EcssTmtcError> {
Ok(VerificationSendable::new_no_token(
@ -783,7 +714,7 @@ impl VerificationReporterCore {
token: VerificationToken<TcState>,
seq_counter: u16,
msg_counter: u16,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
) -> Result<
VerificationSendable<'src_data, TcState, VerifSuccess>,
VerificationErrorWithToken<TcState>,
@ -857,7 +788,7 @@ impl VerificationReporterCore {
seq_count: u16,
msg_counter: u16,
req_id: &RequestId,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
step: Option<&(impl EcssEnumeration + ?Sized)>,
) -> Result<PusTmCreator<'src_data>, EcssTmtcError> {
let mut source_data_len = size_of::<u32>();
@ -901,7 +832,9 @@ impl VerificationReporterCore {
if let Some(step) = step {
source_data_len += step.size();
}
source_data_len += params.failure_data.len();
if let Some(failure_data) = params.failure_data {
source_data_len += failure_data.len();
}
source_buffer_large_enough(src_data_buf.len(), source_data_len)?;
req_id.to_bytes(&mut src_data_buf[0..RequestId::SIZE_AS_BYTES]);
idx += RequestId::SIZE_AS_BYTES;
@ -916,7 +849,9 @@ impl VerificationReporterCore {
.write_to_be_bytes(&mut src_data_buf[idx..idx + params.failure_code.size()])
.map_err(PusError::ByteConversion)?;
idx += params.failure_code.size();
src_data_buf[idx..idx + params.failure_data.len()].copy_from_slice(params.failure_data);
if let Some(failure_data) = params.failure_data {
src_data_buf[idx..idx + failure_data.len()].copy_from_slice(failure_data);
}
let mut sp_header = SpHeader::tm_unseg(self.apid(), seq_count, 0).unwrap();
Ok(self.create_pus_verif_tm_base(
src_data_buf,
@ -934,11 +869,11 @@ impl VerificationReporterCore {
subservice: u8,
msg_counter: u16,
sp_header: &mut SpHeader,
time_stamp: &'src_data [u8],
time_stamp: Option<&'src_data [u8]>,
source_data_len: usize,
) -> PusTmCreator<'src_data> {
let tm_sec_header =
PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, Some(time_stamp));
PusTmSecondaryHeader::new(1, subservice, msg_counter, self.dest_id, time_stamp);
PusTmCreator::new(
sp_header,
tm_sec_header,
@ -1035,7 +970,7 @@ mod alloc_mod {
&self,
token: VerificationToken<TcStateNone>,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
) -> Result<VerificationToken<TcStateAccepted>, VerificationOrSendErrorWithToken<TcStateNone>>
{
let seq_count = self
@ -1090,7 +1025,7 @@ mod alloc_mod {
&self,
token: VerificationToken<TcStateAccepted>,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
) -> Result<
VerificationToken<TcStateStarted>,
VerificationOrSendErrorWithToken<TcStateAccepted>,
@ -1150,7 +1085,7 @@ mod alloc_mod {
&self,
token: &VerificationToken<TcStateStarted>,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
step: impl EcssEnumeration,
) -> Result<(), EcssTmtcError> {
let seq_count = self
@ -1213,7 +1148,7 @@ mod alloc_mod {
&self,
token: VerificationToken<TcState>,
sender: &(impl EcssTmSenderCore + ?Sized),
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
) -> Result<(), VerificationOrSendErrorWithToken<TcState>> {
let seq_count = self
.seq_count_provider
@ -1291,34 +1226,24 @@ mod alloc_mod {
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: &(impl CcsdsPacket + IsPusTelecommand)) -> 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);
}
}
}
impl VerificationReportingProvider for VerificationReporterWithSender {
delegate! {
to self.reporter {
fn add_tc(
&mut self,
pus_tc: &(impl CcsdsPacket + IsPusTelecommand),
) -> VerificationToken<TcStateNone>;
fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone>;
}
}
fn acceptance_success(
pub fn acceptance_success(
&self,
token: VerificationToken<TcStateNone>,
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
) -> Result<VerificationToken<TcStateAccepted>, VerificationOrSendErrorWithToken<TcStateNone>>
{
self.reporter
.acceptance_success(token, self.sender.as_ref(), time_stamp)
}
fn acceptance_failure(
pub fn acceptance_failure(
&self,
token: VerificationToken<TcStateNone>,
params: FailParams,
@ -1327,10 +1252,10 @@ mod alloc_mod {
.acceptance_failure(token, self.sender.as_ref(), params)
}
fn start_success(
pub fn start_success(
&self,
token: VerificationToken<TcStateAccepted>,
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
) -> Result<
VerificationToken<TcStateStarted>,
VerificationOrSendErrorWithToken<TcStateAccepted>,
@ -1339,7 +1264,7 @@ mod alloc_mod {
.start_success(token, self.sender.as_ref(), time_stamp)
}
fn start_failure(
pub fn start_failure(
&self,
token: VerificationToken<TcStateAccepted>,
params: FailParams,
@ -1348,17 +1273,17 @@ mod alloc_mod {
.start_failure(token, self.sender.as_ref(), params)
}
fn step_success(
pub fn step_success(
&self,
token: &VerificationToken<TcStateStarted>,
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
step: impl EcssEnumeration,
) -> Result<(), EcssTmtcError> {
self.reporter
.step_success(token, self.sender.as_ref(), time_stamp, step)
}
fn step_failure(
pub fn step_failure(
&self,
token: VerificationToken<TcStateStarted>,
params: FailParamsWithStep,
@ -1367,16 +1292,16 @@ mod alloc_mod {
.step_failure(token, self.sender.as_ref(), params)
}
fn completion_success<TcState: WasAtLeastAccepted + Copy>(
pub fn completion_success<TcState: WasAtLeastAccepted + Copy>(
&self,
token: VerificationToken<TcState>,
time_stamp: &[u8],
time_stamp: Option<&[u8]>,
) -> Result<(), VerificationOrSendErrorWithToken<TcState>> {
self.reporter
.completion_success(token, self.sender.as_ref(), time_stamp)
}
fn completion_failure<TcState: WasAtLeastAccepted + Copy>(
pub fn completion_failure<TcState: WasAtLeastAccepted + Copy>(
&self,
token: VerificationToken<TcState>,
params: FailParams,
@ -1397,7 +1322,7 @@ mod std_mod {
}
#[cfg(test)]
pub mod tests {
mod tests {
use crate::pool::{PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
use crate::pus::tests::CommonTmInfo;
use crate::pus::verification::{
@ -1410,8 +1335,6 @@ pub mod tests {
use crate::ChannelId;
use alloc::boxed::Box;
use alloc::format;
use alloc::sync::Arc;
use hashbrown::HashMap;
use spacepackets::ecss::tc::{PusTcCreator, PusTcSecondaryHeader};
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, PusError, PusPacket};
@ -1419,208 +1342,15 @@ pub mod tests {
use spacepackets::{ByteConversionError, CcsdsPacket, SpHeader};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::sync::{mpsc, Mutex};
use std::sync::mpsc;
use std::time::Duration;
use std::vec;
use std::vec::Vec;
use super::VerificationReportingProvider;
fn is_send<T: Send>(_: &T) {}
#[allow(dead_code)]
fn is_sync<T: Sync>(_: &T) {}
pub struct VerificationStatus {
pub accepted: Option<bool>,
pub started: Option<bool>,
pub step: u64,
pub step_status: Option<bool>,
pub completed: Option<bool>,
pub failure_data: Option<Vec<u8>>,
pub fail_enum: Option<u64>,
}
pub type SharedVerificationMap = Arc<Mutex<RefCell<HashMap<RequestId, VerificationStatus>>>>;
#[derive(Clone)]
pub struct TestVerificationReporter {
pub verification_map: SharedVerificationMap,
}
impl TestVerificationReporter {
pub fn new(verification_map: SharedVerificationMap) -> Self {
Self { verification_map }
}
}
impl VerificationReportingProvider for TestVerificationReporter {
fn add_tc_with_req_id(&mut self, req_id: RequestId) -> VerificationToken<TcStateNone> {
let verif_map = self.verification_map.lock().unwrap();
verif_map.borrow_mut().insert(
req_id,
VerificationStatus {
accepted: None,
started: None,
step: 0,
step_status: None,
completed: None,
failure_data: None,
fail_enum: None,
},
);
VerificationToken {
state: core::marker::PhantomData,
req_id,
}
}
fn acceptance_success(
&self,
token: VerificationToken<TcStateNone>,
_time_stamp: &[u8],
) -> Result<
VerificationToken<super::TcStateAccepted>,
super::VerificationOrSendErrorWithToken<TcStateNone>,
> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => entry.accepted = Some(true),
None => panic!(
"unexpected acceptance success for request ID {}",
token.req_id()
),
};
Ok(VerificationToken {
state: core::marker::PhantomData,
req_id: token.req_id,
})
}
fn acceptance_failure(
&self,
token: VerificationToken<TcStateNone>,
params: FailParams,
) -> Result<(), super::VerificationOrSendErrorWithToken<TcStateNone>> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => {
entry.accepted = Some(false);
entry.failure_data = Some(params.failure_data.to_vec());
entry.fail_enum = Some(params.failure_code.value());
}
None => panic!(
"unexpected acceptance failure for request ID {}",
token.req_id()
),
};
Ok(())
}
fn start_success(
&self,
token: VerificationToken<super::TcStateAccepted>,
_time_stamp: &[u8],
) -> Result<
VerificationToken<super::TcStateStarted>,
super::VerificationOrSendErrorWithToken<super::TcStateAccepted>,
> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => entry.started = Some(true),
None => panic!("unexpected start success for request ID {}", token.req_id()),
};
Ok(VerificationToken {
state: core::marker::PhantomData,
req_id: token.req_id,
})
}
fn start_failure(
&self,
token: VerificationToken<super::TcStateAccepted>,
params: FailParams,
) -> Result<(), super::VerificationOrSendErrorWithToken<super::TcStateAccepted>> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => {
entry.started = Some(false);
entry.failure_data = Some(params.failure_data.to_vec());
entry.fail_enum = Some(params.failure_code.value());
}
None => panic!("unexpected start failure for request ID {}", token.req_id()),
};
Ok(())
}
fn step_success(
&self,
token: &VerificationToken<super::TcStateStarted>,
_time_stamp: &[u8],
step: impl spacepackets::ecss::EcssEnumeration,
) -> Result<(), EcssTmtcError> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => {
entry.step = step.value();
entry.step_status = Some(true);
}
None => panic!("unexpected start success for request ID {}", token.req_id()),
};
Ok(())
}
fn step_failure(
&self,
token: VerificationToken<super::TcStateStarted>,
_params: FailParamsWithStep,
) -> Result<(), super::VerificationOrSendErrorWithToken<super::TcStateStarted>> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => {
entry.step_status = Some(false);
}
None => panic!("unexpected start success for request ID {}", token.req_id()),
};
Ok(())
}
fn completion_success<TcState: super::WasAtLeastAccepted + Copy>(
&self,
token: VerificationToken<TcState>,
_time_stamp: &[u8],
) -> Result<(), super::VerificationOrSendErrorWithToken<TcState>> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => entry.completed = Some(true),
None => panic!(
"unexpected acceptance success for request ID {}",
token.req_id()
),
};
Ok(())
}
fn completion_failure<TcState: super::WasAtLeastAccepted + Copy>(
&self,
token: VerificationToken<TcState>,
params: FailParams,
) -> Result<(), super::VerificationOrSendErrorWithToken<TcState>> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => {
entry.completed = Some(false);
entry.failure_data = Some(params.failure_data.to_vec());
entry.fail_enum = Some(params.failure_code.value());
}
None => panic!(
"unexpected acceptance success for request ID {}",
token.req_id()
),
};
Ok(())
}
}
const TEST_APID: u16 = 0x02;
const EMPTY_STAMP: [u8; 7] = [0; 7];
@ -1754,7 +1484,7 @@ pub mod tests {
#[test]
fn test_mpsc_verif_send_sync() {
let pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(8, 8)], false));
let pool = StaticMemoryPool::new(StaticPoolConfig::new(vec![(8, 8)]));
let shared_tm_store = SharedTmPool::new(pool);
let (tx, _) = mpsc::channel();
let mpsc_verif_sender =
@ -1774,7 +1504,7 @@ pub mod tests {
fn test_basic_acceptance_success() {
let (b, tok) = base_init(false);
let mut sender = TestSender::default();
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
b.vr.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
acceptance_check(&mut sender, &tok.req_id);
}
@ -1783,7 +1513,7 @@ pub mod tests {
fn test_basic_acceptance_success_with_helper() {
let (mut b, tok) = base_with_helper_init();
b.helper
.acceptance_success(tok, &EMPTY_STAMP)
.acceptance_success(tok, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
acceptance_check(sender, &tok.req_id);
@ -1814,7 +1544,7 @@ pub mod tests {
let stamp_buf = [1, 2, 3, 4, 5, 6, 7];
let mut sender = TestSender::default();
let fail_code = EcssEnumU16::new(2);
let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code);
let fail_params = FailParams::new(Some(stamp_buf.as_slice()), &fail_code, None);
b.vr.acceptance_failure(tok, &sender, fail_params)
.expect("Sending acceptance success failed");
acceptance_fail_check(&mut sender, tok.req_id, stamp_buf);
@ -1826,7 +1556,7 @@ pub mod tests {
b.rep().reporter.dest_id = 5;
let stamp_buf = [1, 2, 3, 4, 5, 6, 7];
let fail_code = EcssEnumU16::new(2);
let fail_params = FailParams::new_no_fail_data(stamp_buf.as_slice(), &fail_code);
let fail_params = FailParams::new(Some(stamp_buf.as_slice()), &fail_code, None);
b.helper
.acceptance_failure(tok, fail_params)
.expect("Sending acceptance success failed");
@ -1843,7 +1573,11 @@ pub mod tests {
let fail_data: [u8; 16] = [0; 16];
// 4 req ID + 1 byte step + 2 byte error code + 8 byte fail data
assert_eq!(b.rep().allowed_source_data_len(), 15);
let fail_params = FailParams::new(stamp_buf.as_slice(), &fail_code, fail_data.as_slice());
let fail_params = FailParams::new(
Some(stamp_buf.as_slice()),
&fail_code,
Some(fail_data.as_slice()),
);
let res = b.helper.acceptance_failure(tok, fail_params);
assert!(res.is_err());
let err_with_token = res.unwrap_err();
@ -1875,7 +1609,11 @@ pub mod tests {
let fail_data = EcssEnumU32::new(12);
let mut fail_data_raw = [0; 4];
fail_data.write_to_be_bytes(&mut fail_data_raw).unwrap();
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice());
let fail_params = FailParams::new(
Some(&EMPTY_STAMP),
&fail_code,
Some(fail_data_raw.as_slice()),
);
b.vr.acceptance_failure(tok, &sender, fail_params)
.expect("Sending acceptance success failed");
let cmp_info = TmInfo {
@ -1935,12 +1673,16 @@ pub mod tests {
let fail_data: i32 = -12;
let mut fail_data_raw = [0; 4];
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice());
let fail_params = FailParams::new(
Some(&EMPTY_STAMP),
&fail_code,
Some(fail_data_raw.as_slice()),
);
let accepted_token =
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
b.vr.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
b.vr.start_failure(accepted_token, &sender, fail_params)
b.vr.start_failure(accepted_token, &mut sender, fail_params)
.expect("Start failure failure");
start_fail_check(&mut sender, tok.req_id, fail_data_raw);
}
@ -1952,15 +1694,21 @@ pub mod tests {
let fail_data: i32 = -12;
let mut fail_data_raw = [0; 4];
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
let fail_params = FailParams::new(&EMPTY_STAMP, &fail_code, fail_data_raw.as_slice());
let fail_params = FailParams::new(
Some(&EMPTY_STAMP),
&fail_code,
Some(fail_data_raw.as_slice()),
);
let accepted_token = b
.helper
.acceptance_success(tok, &EMPTY_STAMP)
.acceptance_success(tok, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
b.helper
let empty = b
.helper
.start_failure(accepted_token, fail_params)
.expect("Start failure failure");
assert_eq!(empty, ());
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
start_fail_check(sender, tok.req_id, fail_data_raw);
}
@ -2027,17 +1775,27 @@ pub mod tests {
let mut sender = TestSender::default();
let accepted_token = b
.rep()
.acceptance_success(tok, &sender, &EMPTY_STAMP)
.acceptance_success(tok, &sender, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token = b
.rep()
.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
.start_success(accepted_token, &sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.rep()
.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(0))
.step_success(
&started_token,
&sender,
Some(&EMPTY_STAMP),
EcssEnumU8::new(0),
)
.expect("Sending step 0 success failed");
b.vr.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(1))
.expect("Sending step 1 success failed");
b.vr.step_success(
&started_token,
&sender,
Some(&EMPTY_STAMP),
EcssEnumU8::new(1),
)
.expect("Sending step 1 success failed");
assert_eq!(sender.service_queue.borrow().len(), 4);
step_success_check(&mut sender, tok.req_id);
}
@ -2047,17 +1805,17 @@ pub mod tests {
let (mut b, tok) = base_with_helper_init();
let accepted_token = b
.helper
.acceptance_success(tok, &EMPTY_STAMP)
.acceptance_success(tok, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token = b
.helper
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.helper
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0))
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(0))
.expect("Sending step 0 success failed");
b.helper
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(1))
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(1))
.expect("Sending step 1 success failed");
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
assert_eq!(sender.service_queue.borrow().len(), 4);
@ -2142,22 +1900,31 @@ pub mod tests {
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
let fail_step = EcssEnumU8::new(1);
let fail_params = FailParamsWithStep::new(
&EMPTY_STAMP,
Some(&EMPTY_STAMP),
&fail_step,
&fail_code,
fail_data_raw.as_slice(),
Some(fail_data_raw.as_slice()),
);
let accepted_token =
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token =
b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.vr.step_success(&started_token, &sender, &EMPTY_STAMP, EcssEnumU8::new(0))
let mut empty =
b.vr.step_success(
&started_token,
&mut sender,
Some(&EMPTY_STAMP),
EcssEnumU8::new(0),
)
.expect("Sending completion success failed");
b.vr.step_failure(started_token, &sender, fail_params)
.expect("Step failure failed");
assert_eq!(empty, ());
empty =
b.vr.step_failure(started_token, &mut sender, fail_params)
.expect("Step failure failed");
assert_eq!(empty, ());
check_step_failure(&mut sender, req_id, fail_data_raw);
}
@ -2171,26 +1938,30 @@ pub mod tests {
fail_data_raw.copy_from_slice(fail_data.to_be_bytes().as_slice());
let fail_step = EcssEnumU8::new(1);
let fail_params = FailParamsWithStep::new(
&EMPTY_STAMP,
Some(&EMPTY_STAMP),
&fail_step,
&fail_code,
fail_data_raw.as_slice(),
Some(fail_data_raw.as_slice()),
);
let accepted_token = b
.helper
.acceptance_success(tok, &EMPTY_STAMP)
.acceptance_success(tok, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token = b
.helper
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.helper
.step_success(&started_token, &EMPTY_STAMP, EcssEnumU8::new(0))
let mut empty = b
.helper
.step_success(&started_token, Some(&EMPTY_STAMP), EcssEnumU8::new(0))
.expect("Sending completion success failed");
b.helper
assert_eq!(empty, ());
empty = b
.helper
.step_failure(started_token, fail_params)
.expect("Step failure failed");
assert_eq!(empty, ());
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
check_step_failure(sender, req_id, fail_data_raw);
}
@ -2247,16 +2018,18 @@ pub mod tests {
let mut sender = TestSender::default();
let req_id = tok.req_id;
let fail_code = EcssEnumU32::new(0x1020);
let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code);
let fail_params = FailParams::new(Some(&EMPTY_STAMP), &fail_code, None);
let accepted_token =
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token =
b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.vr.completion_failure(started_token, &sender, fail_params)
.expect("Completion failure");
let empty =
b.vr.completion_failure(started_token, &mut sender, fail_params)
.expect("Completion failure");
assert_eq!(empty, ());
completion_fail_check(&mut sender, req_id);
}
@ -2265,19 +2038,21 @@ pub mod tests {
let (mut b, tok) = base_with_helper_init();
let req_id = tok.req_id;
let fail_code = EcssEnumU32::new(0x1020);
let fail_params = FailParams::new_no_fail_data(&EMPTY_STAMP, &fail_code);
let fail_params = FailParams::new(Some(&EMPTY_STAMP), &fail_code, None);
let accepted_token = b
.helper
.acceptance_success(tok, &EMPTY_STAMP)
.acceptance_success(tok, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token = b
.helper
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.helper
let empty = b
.helper
.completion_failure(started_token, fail_params)
.expect("Completion failure");
assert_eq!(empty, ());
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
completion_fail_check(sender, req_id);
}
@ -2331,12 +2106,12 @@ pub mod tests {
let (b, tok) = base_init(false);
let mut sender = TestSender::default();
let accepted_token =
b.vr.acceptance_success(tok, &sender, &EMPTY_STAMP)
b.vr.acceptance_success(tok, &mut sender, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token =
b.vr.start_success(accepted_token, &sender, &[0, 1, 0, 1, 0, 1, 0])
b.vr.start_success(accepted_token, &mut sender, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.vr.completion_success(started_token, &sender, &EMPTY_STAMP)
b.vr.completion_success(started_token, &mut sender, Some(&EMPTY_STAMP))
.expect("Sending completion success failed");
completion_success_check(&mut sender, tok.req_id);
}
@ -2346,23 +2121,23 @@ pub mod tests {
let (mut b, tok) = base_with_helper_init();
let accepted_token = b
.helper
.acceptance_success(tok, &EMPTY_STAMP)
.acceptance_success(tok, Some(&EMPTY_STAMP))
.expect("Sending acceptance success failed");
let started_token = b
.helper
.start_success(accepted_token, &[0, 1, 0, 1, 0, 1, 0])
.start_success(accepted_token, Some(&[0, 1, 0, 1, 0, 1, 0]))
.expect("Sending start success failed");
b.helper
.completion_success(started_token, &EMPTY_STAMP)
.completion_success(started_token, Some(&EMPTY_STAMP))
.expect("Sending completion success failed");
let sender: &mut TestSender = b.helper.sender.downcast_mut().unwrap();
completion_success_check(sender, tok.req_id);
}
#[test]
// TODO: maybe a bit more extensive testing, all I have time for right now
fn test_seq_count_increment() {
let pool_cfg =
StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false);
let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
let tm_pool = StaticMemoryPool::new(pool_cfg.clone());
let shared_tm_store = SharedTmPool::new(tm_pool);
let shared_tm_pool = shared_tm_store.clone_backing_pool();
@ -2379,13 +2154,13 @@ pub mod tests {
// Complete success sequence for a telecommand
let accepted_token = reporter
.acceptance_success(init_token, &EMPTY_STAMP)
.acceptance_success(init_token, Some(&EMPTY_STAMP))
.unwrap();
let started_token = reporter
.start_success(accepted_token, &EMPTY_STAMP)
.start_success(accepted_token, Some(&EMPTY_STAMP))
.unwrap();
reporter
.completion_success(started_token, &EMPTY_STAMP)
.completion_success(started_token, Some(&EMPTY_STAMP))
.unwrap();
// Verify it arrives correctly on receiver end

View File

@ -0,0 +1 @@

View File

@ -4,7 +4,6 @@ use spacepackets::ecss::{EcssEnumU16, EcssEnumeration};
use spacepackets::util::UnsignedEnum;
use spacepackets::ByteConversionError;
/// Simple [u16] based result code type which also allows to group related resultcodes.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ResultU16 {
@ -13,7 +12,7 @@ pub struct ResultU16 {
}
impl ResultU16 {
pub const fn new(group_id: u8, unique_id: u8) -> Self {
pub const fn const_new(group_id: u8, unique_id: u8) -> Self {
Self {
group_id,
unique_id,
@ -52,10 +51,6 @@ impl UnsignedEnum for ResultU16 {
buf[1] = self.unique_id;
Ok(self.size())
}
fn value(&self) -> u64 {
self.raw() as u64
}
}
impl EcssEnumeration for ResultU16 {
@ -63,26 +58,3 @@ impl EcssEnumeration for ResultU16 {
16
}
}
#[cfg(test)]
mod tests {
use super::*;
const RESULT_CODE_CONST: ResultU16 = ResultU16::new(1, 1);
#[test]
pub fn test_basic() {
let result_code = ResultU16::new(1, 1);
assert_eq!(result_code.unique_id(), 1);
assert_eq!(result_code.group_id(), 1);
assert_eq!(result_code, RESULT_CODE_CONST);
assert_eq!(result_code.raw(), (1_u16 << 8) | 1);
assert_eq!(result_code.pfc(), 16);
assert_eq!(result_code.size(), 2);
let mut buf: [u8; 2] = [0; 2];
let written = result_code.write_to_be_bytes(&mut buf).unwrap();
assert_eq!(written, 2);
assert_eq!(buf[0], 1);
assert_eq!(buf[1], 1);
}
}

View File

@ -18,8 +18,8 @@
//! # Example
//!
//! ```rust
//! use satrs::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor};
//! use satrs::tmtc::{ReceivesTc, ReceivesTcCore};
//! use satrs_core::tmtc::ccsds_distrib::{CcsdsPacketHandler, CcsdsDistributor};
//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
//! use spacepackets::{CcsdsPacket, SpHeader};
//! use spacepackets::ecss::WritablePusPacket;
//! use spacepackets::ecss::tc::{PusTc, PusTcCreator};

View File

@ -20,6 +20,8 @@ pub use ccsds_distrib::{CcsdsDistributor, CcsdsError, CcsdsPacketHandler};
#[cfg(feature = "alloc")]
pub use pus_distrib::{PusDistributor, PusServiceProvider};
pub type TargetId = u32;
/// Generic trait for object which can receive any telecommands in form of a raw bytestream, with
/// no assumptions about the received protocol.
///

View File

@ -19,8 +19,8 @@
//!
//! ```rust
//! use spacepackets::ecss::WritablePusPacket;
//! use satrs::tmtc::pus_distrib::{PusDistributor, PusServiceProvider};
//! use satrs::tmtc::{ReceivesTc, ReceivesTcCore};
//! use satrs_core::tmtc::pus_distrib::{PusDistributor, PusServiceProvider};
//! use satrs_core::tmtc::{ReceivesTc, ReceivesTcCore};
//! use spacepackets::SpHeader;
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
//! struct ConcretePusHandler {

View File

@ -1,4 +1,4 @@
use satrs::pool::{PoolGuard, PoolProvider, StaticMemoryPool, StaticPoolConfig, StoreAddr};
use satrs_core::pool::{PoolGuard, PoolProvider, StaticMemoryPool, StaticPoolConfig, StoreAddr};
use std::ops::DerefMut;
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
@ -9,7 +9,7 @@ const DUMMY_DATA: [u8; 4] = [0, 1, 2, 3];
#[test]
fn threaded_usage() {
let pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)], false);
let pool_cfg = StaticPoolConfig::new(vec![(16, 6), (32, 3), (8, 12)]);
let shared_pool = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg)));
let shared_clone = shared_pool.clone();
let (tx, rx): (Sender<StoreAddr>, Receiver<StoreAddr>) = mpsc::channel();

View File

@ -1,6 +1,6 @@
#![allow(dead_code, unused_imports)]
use satrs::events::{
use satrs_core::events::{
EventU32, EventU32TypedSev, GenericEvent, HasSeverity, LargestEventRaw, LargestGroupIdRaw,
Severity, SeverityInfo, SeverityLow, SeverityMedium,
};

View File

@ -1,11 +1,13 @@
use satrs::event_man::{
use satrs_core::event_man::{
EventManagerWithMpscQueue, MpscEventU32Receiver, MpscEventU32SendProvider, SendEventProvider,
};
use satrs::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs::params::U32Pair;
use satrs::params::{Params, ParamsHeapless, WritableToBeBytes};
use satrs::pus::event_man::{DefaultPusMgmtBackendProvider, EventReporter, PusEventDispatcher};
use satrs::pus::MpscTmAsVecSender;
use satrs_core::events::{EventU32, EventU32TypedSev, Severity, SeverityInfo};
use satrs_core::params::U32Pair;
use satrs_core::params::{Params, ParamsHeapless, WritableToBeBytes};
use satrs_core::pus::event_man::{
DefaultPusMgmtBackendProvider, EventReporter, PusEventDispatcher,
};
use satrs_core::pus::MpscTmAsVecSender;
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::{PusError, PusPacket};
use std::sync::mpsc::{channel, SendError, TryRecvError};

View File

@ -1,13 +1,14 @@
#[cfg(feature = "crossbeam")]
//#[cfg(feature = "crossbeam")]
pub mod crossbeam_test {
use hashbrown::HashMap;
use satrs::pool::{PoolProvider, PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig};
use satrs::pus::verification::{
FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender,
VerificationReportingProvider,
use satrs_core::pool::{
PoolProvider, PoolProviderWithGuards, StaticMemoryPool, StaticPoolConfig,
};
use satrs::pus::CrossbeamTmInStoreSender;
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs_core::pus::verification::{
FailParams, RequestId, VerificationReporterCfg, VerificationReporterWithSender,
};
use satrs_core::pus::CrossbeamTmInStoreSender;
use satrs_core::tmtc::tm_helper::SharedTmPool;
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
use spacepackets::ecss::tm::PusTmReader;
use spacepackets::ecss::{EcssEnumU16, EcssEnumU8, PusPacket, WritablePusPacket};
@ -34,8 +35,7 @@ pub mod crossbeam_test {
// each reporter have an own sequence count provider.
let cfg = VerificationReporterCfg::new(TEST_APID, 1, 2, 8).unwrap();
// Shared pool object to store the verification PUS telemetry
let pool_cfg =
StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)], false);
let pool_cfg = StaticPoolConfig::new(vec![(10, 32), (10, 64), (10, 128), (10, 1024)]);
let shared_tm_pool = SharedTmPool::new(StaticMemoryPool::new(pool_cfg.clone()));
let shared_tc_pool_0 = Arc::new(RwLock::new(StaticMemoryPool::new(pool_cfg)));
let shared_tc_pool_1 = shared_tc_pool_0.clone();
@ -90,24 +90,24 @@ pub mod crossbeam_test {
let token = reporter_with_sender_0.add_tc_with_req_id(req_id_0);
let accepted_token = reporter_with_sender_0
.acceptance_success(token, &FIXED_STAMP)
.acceptance_success(token, Some(&FIXED_STAMP))
.expect("Acceptance success failed");
// Do some start handling here
let started_token = reporter_with_sender_0
.start_success(accepted_token, &FIXED_STAMP)
.start_success(accepted_token, Some(&FIXED_STAMP))
.expect("Start success failed");
// Do some step handling here
reporter_with_sender_0
.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(0))
.step_success(&started_token, Some(&FIXED_STAMP), EcssEnumU8::new(0))
.expect("Start success failed");
// Finish up
reporter_with_sender_0
.step_success(&started_token, &FIXED_STAMP, EcssEnumU8::new(1))
.step_success(&started_token, Some(&FIXED_STAMP), EcssEnumU8::new(1))
.expect("Start success failed");
reporter_with_sender_0
.completion_success(started_token, &FIXED_STAMP)
.completion_success(started_token, Some(&FIXED_STAMP))
.expect("Completion success failed");
});
@ -125,13 +125,13 @@ pub mod crossbeam_test {
let (tc, _) = PusTcReader::new(&tc_buf[0..tc_len]).unwrap();
let token = reporter_with_sender_1.add_tc(&tc);
let accepted_token = reporter_with_sender_1
.acceptance_success(token, &FIXED_STAMP)
.acceptance_success(token, Some(&FIXED_STAMP))
.expect("Acceptance success failed");
let started_token = reporter_with_sender_1
.start_success(accepted_token, &FIXED_STAMP)
.start_success(accepted_token, Some(&FIXED_STAMP))
.expect("Start success failed");
let fail_code = EcssEnumU16::new(2);
let params = FailParams::new_no_fail_data(&FIXED_STAMP, &fail_code);
let params = FailParams::new(Some(&FIXED_STAMP), &fail_code, None);
reporter_with_sender_1
.completion_failure(started_token, params)
.expect("Completion success failed");

View File

@ -22,7 +22,7 @@ use std::{
};
use hashbrown::HashSet;
use satrs::{
use satrs_core::{
encoding::cobs::encode_packet_with_cobs,
hal::std::tcp_server::{ServerConfig, TcpSpacepacketsServer, TcpTmtcInCobsServer},
tmtc::{ReceivesTcCore, TmPacketSourceCore},

View File

@ -1,6 +1,6 @@
[package]
name = "satrs-example"
version = "0.1.1"
version = "0.1.0"
edition = "2021"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
default-run = "satrs-example"
@ -19,13 +19,13 @@ num_enum = "0.7"
thiserror = "1"
derive-new = "0.5"
[dependencies.satrs]
version = "0.2.0-rc.0"
# path = "../satrs"
[dependencies.satrs-core]
# version = "0.1.0-alpha.1"
path = "../satrs-core"
[dependencies.satrs-mib]
version = "0.1.1"
# path = "../satrs-mib"
# version = "0.1.0-alpha.1"
path = "../satrs-mib"
[features]
dyn_tmtc = []

View File

@ -1,11 +1,10 @@
use std::sync::mpsc::{self, TryRecvError};
use log::{info, warn};
use satrs::pus::verification::{VerificationReporterWithSender, VerificationReportingProvider};
use satrs::pus::{EcssTmSender, PusTmWrapper};
use satrs::request::TargetAndApidId;
use satrs::spacepackets::ecss::hk::Subservice as HkSubservice;
use satrs::{
use satrs_core::pus::verification::VerificationReporterWithSender;
use satrs_core::pus::{EcssTmSender, PusTmWrapper};
use satrs_core::spacepackets::ecss::hk::Subservice as HkSubservice;
use satrs_core::{
hk::HkRequest,
spacepackets::{
ecss::tm::{PusTmCreator, PusTmSecondaryHeader},
@ -71,12 +70,12 @@ impl AcsTask {
"ACS thread: Received HK request {:?}",
request.targeted_request
);
let target_and_apid_id = TargetAndApidId::from(request.targeted_request.target_id);
match request.targeted_request.request {
Request::Hk(hk_req) => match hk_req {
HkRequest::OneShot(unique_id) => {
self.handle_hk_request(target_and_apid_id.target(), unique_id)
}
HkRequest::OneShot(unique_id) => self.handle_hk_request(
request.targeted_request.target_id_with_apid.target_id(),
unique_id,
),
HkRequest::Enable(_) => {}
HkRequest::Disable(_) => {}
HkRequest::ModifyCollectionInterval(_, _) => {}
@ -90,10 +89,10 @@ impl AcsTask {
}
let started_token = self
.verif_reporter
.start_success(request.token, &self.timestamp)
.start_success(request.token, Some(&self.timestamp))
.expect("Sending start success failed");
self.verif_reporter
.completion_success(started_token, &self.timestamp)
.completion_success(started_token, Some(&self.timestamp))
.expect("Sending completion success failed");
true
}

View File

@ -1,7 +1,7 @@
use satrs::pus::verification::RequestId;
use satrs::spacepackets::ecss::tc::PusTcCreator;
use satrs::spacepackets::ecss::tm::PusTmReader;
use satrs::{
use satrs_core::pus::verification::RequestId;
use satrs_core::spacepackets::ecss::tc::PusTcCreator;
use satrs_core::spacepackets::ecss::tm::PusTmReader;
use satrs_core::{
spacepackets::ecss::{PusPacket, WritablePusPacket},
spacepackets::SpHeader,
};

View File

@ -1,6 +1,6 @@
use satrs::pus::ReceivesEcssPusTc;
use satrs::spacepackets::{CcsdsPacket, SpHeader};
use satrs::tmtc::{CcsdsPacketHandler, ReceivesCcsdsTc};
use satrs_core::pus::ReceivesEcssPusTc;
use satrs_core::spacepackets::{CcsdsPacket, SpHeader};
use satrs_core::tmtc::{CcsdsPacketHandler, ReceivesCcsdsTc};
use satrs_example::config::PUS_APID;
#[derive(Clone)]

View File

@ -1,10 +1,10 @@
use satrs::res_code::ResultU16;
use satrs_core::res_code::ResultU16;
use satrs_mib::res_code::ResultU16Info;
use satrs_mib::resultcode;
use std::net::Ipv4Addr;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use satrs::{
use satrs_core::{
events::{EventU32TypedSev, SeverityInfo},
pool::{StaticMemoryPool, StaticPoolConfig},
};
@ -42,31 +42,24 @@ pub mod tmtc_err {
use super::*;
#[resultcode]
pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 0);
pub const INVALID_PUS_SERVICE: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 0);
#[resultcode]
pub const INVALID_PUS_SUBSERVICE: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 1);
pub const INVALID_PUS_SUBSERVICE: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 1);
#[resultcode]
pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2);
pub const PUS_SERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 2);
#[resultcode]
pub const PUS_SUBSERVICE_NOT_IMPLEMENTED: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 3);
#[resultcode]
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 4);
#[resultcode]
pub const ROUTING_ERROR: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 5);
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 3);
#[resultcode(
info = "Not enough data inside the TC application data field. Optionally includes: \
8 bytes of failure data containing 2 failure parameters, \
P1 (u32 big endian): Expected data length, P2: Found data length"
)]
pub const NOT_ENOUGH_APP_DATA: ResultU16 = ResultU16::new(GroupId::Tmtc as u8, 2);
pub const NOT_ENOUGH_APP_DATA: ResultU16 = ResultU16::const_new(GroupId::Tmtc as u8, 2);
pub const TMTC_RESULTS: &[ResultU16Info] = &[
INVALID_PUS_SERVICE_EXT,
INVALID_PUS_SUBSERVICE_EXT,
PUS_SERVICE_NOT_IMPLEMENTED_EXT,
UNKNOWN_TARGET_ID_EXT,
ROUTING_ERROR_EXT,
NOT_ENOUGH_APP_DATA_EXT,
];
}
@ -76,20 +69,13 @@ pub mod hk_err {
use super::*;
#[resultcode]
pub const TARGET_ID_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 0);
pub const TARGET_ID_MISSING: ResultU16 = ResultU16::const_new(GroupId::Hk as u8, 0);
#[resultcode]
pub const UNIQUE_ID_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 1);
pub const UNIQUE_ID_MISSING: ResultU16 = ResultU16::const_new(GroupId::Hk as u8, 1);
#[resultcode]
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::new(GroupId::Hk as u8, 2);
pub const UNKNOWN_TARGET_ID: ResultU16 = ResultU16::const_new(GroupId::Hk as u8, 2);
#[resultcode]
pub const COLLECTION_INTERVAL_MISSING: ResultU16 = ResultU16::new(GroupId::Hk as u8, 3);
pub const HK_ERR_RESULTS: &[ResultU16Info] = &[
TARGET_ID_MISSING_EXT,
UNKNOWN_TARGET_ID_EXT,
UNKNOWN_TARGET_ID_EXT,
COLLECTION_INTERVAL_MISSING_EXT,
];
pub const COLLECTION_INTERVAL_MISSING: ResultU16 = ResultU16::const_new(GroupId::Hk as u8, 3);
}
#[allow(clippy::enum_variant_names)]
@ -117,43 +103,34 @@ pub mod pool {
use super::*;
pub fn create_static_pools() -> (StaticMemoryPool, StaticMemoryPool) {
(
StaticMemoryPool::new(StaticPoolConfig::new(
vec![
(30, 32),
(15, 64),
(15, 128),
(15, 256),
(15, 1024),
(15, 2048),
],
true,
)),
StaticMemoryPool::new(StaticPoolConfig::new(
vec![
(30, 32),
(15, 64),
(15, 128),
(15, 256),
(15, 1024),
(15, 2048),
],
true,
)),
)
}
pub fn create_sched_tc_pool() -> StaticMemoryPool {
StaticMemoryPool::new(StaticPoolConfig::new(
vec![
StaticMemoryPool::new(StaticPoolConfig::new(vec![
(30, 32),
(15, 64),
(15, 128),
(15, 256),
(15, 1024),
(15, 2048),
],
true,
))
])),
StaticMemoryPool::new(StaticPoolConfig::new(vec![
(30, 32),
(15, 64),
(15, 128),
(15, 256),
(15, 1024),
(15, 2048),
])),
)
}
pub fn create_sched_tc_pool() -> StaticMemoryPool {
StaticMemoryPool::new(StaticPoolConfig::new(vec![
(30, 32),
(15, 64),
(15, 128),
(15, 256),
(15, 1024),
(15, 2048),
]))
}
}

View File

@ -1,6 +1,6 @@
use std::sync::mpsc::{self, SendError};
use satrs::{
use satrs_core::{
event_man::{
EventManager, EventManagerWithMpscQueue, MpscEventReceiver, MpscEventU32SendProvider,
SendEventProvider,
@ -12,10 +12,7 @@ use satrs::{
DefaultPusMgmtBackendProvider, EventReporter, EventRequest, EventRequestWithToken,
PusEventDispatcher,
},
verification::{
TcStateStarted, VerificationReporterWithSender, VerificationReportingProvider,
VerificationToken,
},
verification::{TcStateStarted, VerificationReporterWithSender, VerificationToken},
EcssTmSender,
},
spacepackets::time::cds::{self, TimeProvider},
@ -76,7 +73,7 @@ impl PusEventHandler {
.try_into()
.expect("expected start verification token");
self.verif_handler
.completion_success(started_token, timestamp)
.completion_success(started_token, Some(timestamp))
.expect("Sending completion success failed");
};
// handle event requests

View File

@ -1,5 +1,5 @@
use derive_new::new;
use satrs::spacepackets::ByteConversionError;
use satrs_core::spacepackets::ByteConversionError;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum AcsHkIds {

View File

@ -1 +1,59 @@
use derive_new::new;
use satrs_core::spacepackets::ecss::tc::IsPusTelecommand;
use satrs_core::spacepackets::ecss::PusPacket;
use satrs_core::spacepackets::{ByteConversionError, CcsdsPacket};
use satrs_core::tmtc::TargetId;
use std::fmt;
use thiserror::Error;
pub mod config;
pub type Apid = u16;
#[derive(Debug, Error)]
pub enum TargetIdCreationError {
#[error("byte conversion")]
ByteConversion(#[from] ByteConversionError),
#[error("not enough app data to generate target ID")]
NotEnoughAppData(usize),
}
// TODO: can these stay pub?
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, new)]
pub struct TargetIdWithApid {
pub apid: Apid,
pub target: TargetId,
}
impl fmt::Display for TargetIdWithApid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}, {}", self.apid, self.target)
}
}
impl TargetIdWithApid {
pub fn apid(&self) -> Apid {
self.apid
}
pub fn target_id(&self) -> TargetId {
self.target
}
}
impl TargetIdWithApid {
pub fn from_tc(
tc: &(impl CcsdsPacket + PusPacket + IsPusTelecommand),
) -> Result<Self, TargetIdCreationError> {
if tc.user_data().len() < 4 {
return Err(ByteConversionError::FromSliceTooSmall {
found: tc.user_data().len(),
expected: 8,
}
.into());
}
Ok(Self {
apid: tc.apid(),
target: u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap()),
})
}
}

View File

@ -15,10 +15,9 @@ use crate::pus::stack::PusStack;
use crate::tm_funnel::{TmFunnelDynamic, TmFunnelStatic};
use log::info;
use pus::test::create_test_service_dynamic;
use satrs::hal::std::tcp_server::ServerConfig;
use satrs::hal::std::udp_server::UdpTcServer;
use satrs::request::TargetAndApidId;
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs_core::hal::std::tcp_server::ServerConfig;
use satrs_core::hal::std::udp_server::UdpTcServer;
use satrs_core::tmtc::tm_helper::SharedTmPool;
use satrs_example::config::pool::{create_sched_tc_pool, create_static_pools};
use satrs_example::config::tasks::{
FREQ_MS_AOCS, FREQ_MS_EVENT_HANDLING, FREQ_MS_PUS_STACK, FREQ_MS_UDP_TMTC,
@ -36,18 +35,20 @@ use crate::pus::hk::{create_hk_service_dynamic, create_hk_service_static};
use crate::pus::scheduler::{create_scheduler_service_dynamic, create_scheduler_service_static};
use crate::pus::test::create_test_service_static;
use crate::pus::{PusReceiver, PusTcMpscRouter};
use crate::requests::{GenericRequestRouter, RequestWithToken};
use crate::requests::RequestWithToken;
use crate::tcp::{SyncTcpTmSource, TcpTask};
use crate::tmtc::{
PusTcSourceProviderSharedPool, SharedTcPool, TcSourceTaskDynamic, TcSourceTaskStatic,
};
use crate::udp::{StaticUdpTmHandler, UdpTmtcServer};
use satrs::pus::event_man::EventRequestWithToken;
use satrs::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
use satrs::pus::{EcssTmSender, MpscTmAsVecSender, MpscTmInSharedPoolSender};
use satrs::spacepackets::{time::cds::TimeProvider, time::TimeWriter};
use satrs::tmtc::CcsdsDistributor;
use satrs::ChannelId;
use satrs_core::pus::event_man::EventRequestWithToken;
use satrs_core::pus::verification::{VerificationReporterCfg, VerificationReporterWithSender};
use satrs_core::pus::{EcssTmSender, MpscTmAsVecSender, MpscTmInSharedPoolSender};
use satrs_core::spacepackets::{time::cds::TimeProvider, time::TimeWriter};
use satrs_core::tmtc::{CcsdsDistributor, TargetId};
use satrs_core::ChannelId;
use satrs_example::TargetIdWithApid;
use std::collections::HashMap;
use std::net::{IpAddr, SocketAddr};
use std::sync::mpsc::{self, channel};
use std::sync::{Arc, RwLock};
@ -81,11 +82,11 @@ fn static_tmtc_pool_main() {
tm_funnel_tx.clone(),
));
let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32);
let acs_target_id = TargetIdWithApid::new(PUS_APID, RequestTargetId::AcsSubsystem as TargetId);
let (acs_thread_tx, acs_thread_rx) = channel::<RequestWithToken>();
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
let mut request_map = GenericRequestRouter::default();
request_map.0.insert(acs_target_id.into(), acs_thread_tx);
let mut request_map = HashMap::new();
request_map.insert(acs_target_id, acs_thread_tx);
// This helper structure is used by all telecommand providers which need to send telecommands
// to the TC source.
@ -309,11 +310,11 @@ fn dyn_tmtc_pool_main() {
tm_funnel_tx.clone(),
));
let acs_target_id = TargetAndApidId::new(PUS_APID, RequestTargetId::AcsSubsystem as u32);
let acs_target_id = TargetIdWithApid::new(PUS_APID, RequestTargetId::AcsSubsystem as TargetId);
let (acs_thread_tx, acs_thread_rx) = channel::<RequestWithToken>();
// Some request are targetable. This map is used to retrieve sender handles based on a target ID.
let mut request_map = GenericRequestRouter::default();
request_map.0.insert(acs_target_id.into(), acs_thread_tx);
let mut request_map = HashMap::new();
request_map.insert(acs_target_id, acs_thread_tx);
let tc_source = PusTcSourceProviderDynamic(tc_source_tx);

View File

@ -1,76 +1,22 @@
use crate::requests::{ActionRequest, Request, RequestWithToken};
use log::{error, warn};
use satrs::action::ActionRequest;
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs::pus::action::{PusActionToRequestConverter, PusService8ActionHandler};
use satrs::pus::verification::{
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationReportingProvider,
VerificationToken,
use satrs_core::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs_core::pus::verification::{
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationToken,
};
use satrs::pus::{
use satrs_core::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
PusPacketHandlingError, PusServiceHelper,
EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender,
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
};
use satrs::request::TargetAndApidId;
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusPacket;
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs::{ChannelId, TargetId};
use satrs_core::spacepackets::ecss::tc::PusTcReader;
use satrs_core::spacepackets::ecss::PusPacket;
use satrs_core::tmtc::tm_helper::SharedTmPool;
use satrs_core::ChannelId;
use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
use std::sync::mpsc::{self};
use crate::requests::GenericRequestRouter;
use super::GenericRoutingErrorHandler;
#[derive(Default)]
pub struct ExampleActionRequestConverter {}
impl PusActionToRequestConverter for ExampleActionRequestConverter {
type Error = PusPacketHandlingError;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, ActionRequest), Self::Error> {
let subservice = tc.subservice();
let user_data = tc.user_data();
if user_data.len() < 8 {
verif_reporter
.start_failure(
token,
FailParams::new_no_fail_data(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA),
)
.expect("Sending start failure failed");
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 8,
found: user_data.len(),
});
}
let target_id = TargetAndApidId::from_pus_tc(tc).unwrap();
let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap());
if subservice == 128 {
Ok((
target_id.raw(),
ActionRequest::UnsignedIdAndVecData {
action_id,
data: user_data[8..].to_vec(),
},
))
} else {
verif_reporter
.start_failure(
token,
FailParams::new_no_fail_data(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE),
)
.expect("Sending start failure failed");
Err(PusPacketHandlingError::InvalidSubservice(subservice))
}
}
}
use satrs_example::TargetIdWithApid;
use std::collections::HashMap;
use std::sync::mpsc::{self, Sender};
pub fn create_action_service_static(
shared_tm_store: SharedTmPool,
@ -78,7 +24,7 @@ pub fn create_action_service_static(
verif_reporter: VerificationReporterWithSender,
tc_pool: SharedStaticMemoryPool,
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
action_router: GenericRequestRouter,
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
) -> Pus8Wrapper<EcssTcInSharedStoreConverter> {
let action_srv_tm_sender = MpscTmInSharedPoolSender::new(
TmSenderId::PusAction as ChannelId,
@ -92,16 +38,12 @@ pub fn create_action_service_static(
pus_action_rx,
);
let pus_8_handler = PusService8ActionHandler::new(
PusServiceHelper::new(
Box::new(action_srv_receiver),
Box::new(action_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
),
ExampleActionRequestConverter::default(),
action_router,
GenericRoutingErrorHandler::<8>::default(),
Box::new(action_srv_receiver),
Box::new(action_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInSharedStoreConverter::new(tc_pool.clone(), 2048),
request_map.clone(),
);
Pus8Wrapper { pus_8_handler }
}
@ -110,7 +52,7 @@ pub fn create_action_service_dynamic(
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
verif_reporter: VerificationReporterWithSender,
pus_action_rx: mpsc::Receiver<EcssTcAndToken>,
action_router: GenericRequestRouter,
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
) -> Pus8Wrapper<EcssTcInVecConverter> {
let action_srv_tm_sender = MpscTmAsVecSender::new(
TmSenderId::PusAction as ChannelId,
@ -123,28 +65,146 @@ pub fn create_action_service_dynamic(
pus_action_rx,
);
let pus_8_handler = PusService8ActionHandler::new(
PusServiceHelper::new(
Box::new(action_srv_receiver),
Box::new(action_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInVecConverter::default(),
),
ExampleActionRequestConverter::default(),
action_router,
GenericRoutingErrorHandler::<8>::default(),
Box::new(action_srv_receiver),
Box::new(action_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInVecConverter::default(),
request_map.clone(),
);
Pus8Wrapper { pus_8_handler }
}
pub struct PusService8ActionHandler<TcInMemConverter: EcssTcInMemConverter> {
service_helper: PusServiceHelper<TcInMemConverter>,
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
}
impl<TcInMemConverter: EcssTcInMemConverter> PusService8ActionHandler<TcInMemConverter> {
pub fn new(
tc_receiver: Box<dyn EcssTcReceiver>,
tm_sender: Box<dyn EcssTmSender>,
tm_apid: u16,
verification_handler: VerificationReporterWithSender,
tc_in_mem_converter: TcInMemConverter,
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
) -> Self {
Self {
service_helper: PusServiceHelper::new(
tc_receiver,
tm_sender,
tm_apid,
verification_handler,
tc_in_mem_converter,
),
request_handlers,
}
}
fn handle_action_request_with_id(
&self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
) -> Result<(), PusPacketHandlingError> {
let user_data = tc.user_data();
if user_data.len() < 8 {
self.service_helper
.common
.verification_handler
.borrow_mut()
.start_failure(
token,
FailParams::new(Some(time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None),
)
.expect("Sending start failure failed");
return Err(PusPacketHandlingError::NotEnoughAppData(
"Expected at least 4 bytes".into(),
));
}
//let target_id = u32::from_be_bytes(user_data[0..4].try_into().unwrap());
let target_id = TargetIdWithApid::from_tc(tc).unwrap();
let action_id = u32::from_be_bytes(user_data[4..8].try_into().unwrap());
if let Some(sender) = self.request_handlers.get(&target_id) {
sender
.send(RequestWithToken::new(
target_id,
Request::Action(ActionRequest::CmdWithU32Id((
action_id,
Vec::from(&user_data[8..]),
))),
token,
))
.expect("Forwarding action request failed");
} else {
let mut fail_data: [u8; 4] = [0; 4];
fail_data.copy_from_slice(&target_id.target.to_be_bytes());
self.service_helper
.common
.verification_handler
.borrow_mut()
.start_failure(
token,
FailParams::new(
Some(time_stamp),
&tmtc_err::UNKNOWN_TARGET_ID,
Some(&fail_data),
),
)
.expect("Sending start failure failed");
return Err(PusPacketHandlingError::Other(format!(
"Unknown target ID {target_id}"
)));
}
Ok(())
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.service_helper.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty);
}
let ecss_tc_and_token = possible_packet.unwrap();
self.service_helper
.tc_in_mem_converter
.cache_ecss_tc_in_memory(&ecss_tc_and_token.tc_in_memory)?;
let tc = PusTcReader::new(self.service_helper.tc_in_mem_converter.tc_slice_raw())?.0;
let subservice = tc.subservice();
let mut partial_error = None;
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
match subservice {
128 => {
self.handle_action_request_with_id(ecss_tc_and_token.token, &tc, &time_stamp)?;
}
_ => {
let fail_data = [subservice];
self.service_helper
.common
.verification_handler
.get_mut()
.start_failure(
ecss_tc_and_token.token,
FailParams::new(
Some(&time_stamp),
&tmtc_err::INVALID_PUS_SUBSERVICE,
Some(&fail_data),
),
)
.expect("Sending start failure failed");
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
}
}
if let Some(partial_error) = partial_error {
return Ok(PusPacketHandlerResult::RequestHandledPartialSuccess(
partial_error,
));
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
pub struct Pus8Wrapper<TcInMemConverter: EcssTcInMemConverter> {
pub(crate) pus_8_handler: PusService8ActionHandler<
TcInMemConverter,
VerificationReporterWithSender,
ExampleActionRequestConverter,
GenericRequestRouter,
GenericRoutingErrorHandler<8>,
>,
pub(crate) pus_8_handler: PusService8ActionHandler<TcInMemConverter>,
}
impl<TcInMemConverter: EcssTcInMemConverter> Pus8Wrapper<TcInMemConverter> {

View File

@ -1,17 +1,17 @@
use std::sync::mpsc;
use log::{error, warn};
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs::pus::event_man::EventRequestWithToken;
use satrs::pus::event_srv::PusService5EventHandler;
use satrs::pus::verification::VerificationReporterWithSender;
use satrs::pus::{
use satrs_core::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs_core::pus::event_man::EventRequestWithToken;
use satrs_core::pus::event_srv::PusService5EventHandler;
use satrs_core::pus::verification::VerificationReporterWithSender;
use satrs_core::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
PusServiceHelper,
};
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs::ChannelId;
use satrs_core::tmtc::tm_helper::SharedTmPool;
use satrs_core::ChannelId;
use satrs_example::config::{TcReceiverId, TmSenderId, PUS_APID};
pub fn create_event_service_static(
@ -76,7 +76,7 @@ pub fn create_event_service_dynamic(
}
pub struct Pus5Wrapper<TcInMemConverter: EcssTcInMemConverter> {
pub pus_5_handler: PusService5EventHandler<TcInMemConverter, VerificationReporterWithSender>,
pub pus_5_handler: PusService5EventHandler<TcInMemConverter>,
}
impl<TcInMemConverter: EcssTcInMemConverter> Pus5Wrapper<TcInMemConverter> {

View File

@ -1,145 +1,22 @@
use crate::requests::{Request, RequestWithToken};
use log::{error, warn};
use satrs::hk::{CollectionIntervalFactor, HkRequest};
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs::pus::hk::{PusHkToRequestConverter, PusService3HkHandler};
use satrs::pus::verification::{
FailParams, TcStateAccepted, VerificationReporterWithSender, VerificationReportingProvider,
VerificationToken,
use satrs_core::hk::{CollectionIntervalFactor, HkRequest};
use satrs_core::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs_core::pus::verification::{
FailParams, StdVerifReporterWithSender, VerificationReporterWithSender,
};
use satrs::pus::{
use satrs_core::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
PusPacketHandlingError, PusServiceHelper,
EcssTcReceiver, EcssTmSender, MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender,
PusPacketHandlerResult, PusPacketHandlingError, PusServiceBase, PusServiceHelper,
};
use satrs::request::TargetAndApidId;
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::{hk, PusPacket};
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs::{ChannelId, TargetId};
use satrs_core::spacepackets::ecss::{hk, PusPacket};
use satrs_core::tmtc::tm_helper::SharedTmPool;
use satrs_core::ChannelId;
use satrs_example::config::{hk_err, tmtc_err, TcReceiverId, TmSenderId, PUS_APID};
use std::sync::mpsc::{self};
use crate::requests::GenericRequestRouter;
use super::GenericRoutingErrorHandler;
#[derive(Default)]
pub struct ExampleHkRequestConverter {}
impl PusHkToRequestConverter for ExampleHkRequestConverter {
type Error = PusPacketHandlingError;
fn convert(
&mut self,
token: VerificationToken<TcStateAccepted>,
tc: &PusTcReader,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) -> Result<(TargetId, HkRequest), Self::Error> {
let user_data = tc.user_data();
if user_data.is_empty() {
let user_data_len = user_data.len() as u32;
let user_data_len_raw = user_data_len.to_be_bytes();
verif_reporter
.start_failure(
token,
FailParams::new(
time_stamp,
&tmtc_err::NOT_ENOUGH_APP_DATA,
&user_data_len_raw,
),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 4,
found: 0,
});
}
if user_data.len() < 8 {
let err = if user_data.len() < 4 {
&hk_err::TARGET_ID_MISSING
} else {
&hk_err::UNIQUE_ID_MISSING
};
let user_data_len = user_data.len() as u32;
let user_data_len_raw = user_data_len.to_be_bytes();
verif_reporter
.start_failure(token, FailParams::new(time_stamp, err, &user_data_len_raw))
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 8,
found: 4,
});
}
let subservice = tc.subservice();
let target_id = TargetAndApidId::from_pus_tc(tc).expect("invalid tc format");
let unique_id = u32::from_be_bytes(tc.user_data()[4..8].try_into().unwrap());
let standard_subservice = hk::Subservice::try_from(subservice);
if standard_subservice.is_err() {
verif_reporter
.start_failure(
token,
FailParams::new(time_stamp, &tmtc_err::INVALID_PUS_SUBSERVICE, &[subservice]),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
}
Ok((
target_id.into(),
match standard_subservice.unwrap() {
hk::Subservice::TcEnableHkGeneration | hk::Subservice::TcEnableDiagGeneration => {
HkRequest::Enable(unique_id)
}
hk::Subservice::TcDisableHkGeneration | hk::Subservice::TcDisableDiagGeneration => {
HkRequest::Disable(unique_id)
}
hk::Subservice::TcReportHkReportStructures => todo!(),
hk::Subservice::TmHkPacket => todo!(),
hk::Subservice::TcGenerateOneShotHk | hk::Subservice::TcGenerateOneShotDiag => {
HkRequest::OneShot(unique_id)
}
hk::Subservice::TcModifyDiagCollectionInterval
| hk::Subservice::TcModifyHkCollectionInterval => {
if user_data.len() < 12 {
verif_reporter
.start_failure(
token,
FailParams::new_no_fail_data(
time_stamp,
&tmtc_err::NOT_ENOUGH_APP_DATA,
),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData {
expected: 12,
found: user_data.len(),
});
}
HkRequest::ModifyCollectionInterval(
unique_id,
CollectionIntervalFactor::from_be_bytes(
user_data[8..12].try_into().unwrap(),
),
)
}
_ => {
verif_reporter
.start_failure(
token,
FailParams::new(
time_stamp,
&tmtc_err::PUS_SUBSERVICE_NOT_IMPLEMENTED,
&[subservice],
),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::InvalidSubservice(subservice));
}
},
))
}
}
use satrs_example::TargetIdWithApid;
use std::collections::HashMap;
use std::sync::mpsc::{self, Sender};
pub fn create_hk_service_static(
shared_tm_store: SharedTmPool,
@ -147,7 +24,7 @@ pub fn create_hk_service_static(
verif_reporter: VerificationReporterWithSender,
tc_pool: SharedStaticMemoryPool,
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
request_router: GenericRequestRouter,
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
) -> Pus3Wrapper<EcssTcInSharedStoreConverter> {
let hk_srv_tm_sender = MpscTmInSharedPoolSender::new(
TmSenderId::PusHk as ChannelId,
@ -158,16 +35,12 @@ pub fn create_hk_service_static(
let hk_srv_receiver =
MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx);
let pus_3_handler = PusService3HkHandler::new(
PusServiceHelper::new(
Box::new(hk_srv_receiver),
Box::new(hk_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInSharedStoreConverter::new(tc_pool, 2048),
),
ExampleHkRequestConverter::default(),
request_router,
GenericRoutingErrorHandler::default(),
Box::new(hk_srv_receiver),
Box::new(hk_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInSharedStoreConverter::new(tc_pool, 2048),
request_map,
);
Pus3Wrapper { pus_3_handler }
}
@ -176,7 +49,7 @@ pub fn create_hk_service_dynamic(
tm_funnel_tx: mpsc::Sender<Vec<u8>>,
verif_reporter: VerificationReporterWithSender,
pus_hk_rx: mpsc::Receiver<EcssTcAndToken>,
request_router: GenericRequestRouter,
request_map: HashMap<TargetIdWithApid, mpsc::Sender<RequestWithToken>>,
) -> Pus3Wrapper<EcssTcInVecConverter> {
let hk_srv_tm_sender = MpscTmAsVecSender::new(
TmSenderId::PusHk as ChannelId,
@ -186,28 +59,154 @@ pub fn create_hk_service_dynamic(
let hk_srv_receiver =
MpscTcReceiver::new(TcReceiverId::PusHk as ChannelId, "PUS_8_TC_RECV", pus_hk_rx);
let pus_3_handler = PusService3HkHandler::new(
PusServiceHelper::new(
Box::new(hk_srv_receiver),
Box::new(hk_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInVecConverter::default(),
),
ExampleHkRequestConverter::default(),
request_router,
GenericRoutingErrorHandler::default(),
Box::new(hk_srv_receiver),
Box::new(hk_srv_tm_sender),
PUS_APID,
verif_reporter.clone(),
EcssTcInVecConverter::default(),
request_map,
);
Pus3Wrapper { pus_3_handler }
}
pub struct PusService3HkHandler<TcInMemConverter: EcssTcInMemConverter> {
psb: PusServiceHelper<TcInMemConverter>,
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
}
impl<TcInMemConverter: EcssTcInMemConverter> PusService3HkHandler<TcInMemConverter> {
pub fn new(
tc_receiver: Box<dyn EcssTcReceiver>,
tm_sender: Box<dyn EcssTmSender>,
tm_apid: u16,
verification_handler: StdVerifReporterWithSender,
tc_in_mem_converter: TcInMemConverter,
request_handlers: HashMap<TargetIdWithApid, Sender<RequestWithToken>>,
) -> Self {
Self {
psb: PusServiceHelper::new(
tc_receiver,
tm_sender,
tm_apid,
verification_handler,
tc_in_mem_converter,
),
request_handlers,
}
}
fn handle_one_tc(&mut self) -> Result<PusPacketHandlerResult, PusPacketHandlingError> {
let possible_packet = self.psb.retrieve_and_accept_next_packet()?;
if possible_packet.is_none() {
return Ok(PusPacketHandlerResult::Empty);
}
let ecss_tc_and_token = possible_packet.unwrap();
let tc = self
.psb
.tc_in_mem_converter
.convert_ecss_tc_in_memory_to_reader(&ecss_tc_and_token.tc_in_memory)?;
let subservice = tc.subservice();
let mut partial_error = None;
let time_stamp = PusServiceBase::get_current_timestamp(&mut partial_error);
let user_data = tc.user_data();
if user_data.is_empty() {
self.psb
.common
.verification_handler
.borrow_mut()
.start_failure(
ecss_tc_and_token.token,
FailParams::new(Some(&time_stamp), &tmtc_err::NOT_ENOUGH_APP_DATA, None),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData(
"Expected at least 8 bytes of app data".into(),
));
}
if user_data.len() < 8 {
let err = if user_data.len() < 4 {
&hk_err::TARGET_ID_MISSING
} else {
&hk_err::UNIQUE_ID_MISSING
};
self.psb
.common
.verification_handler
.borrow_mut()
.start_failure(
ecss_tc_and_token.token,
FailParams::new(Some(&time_stamp), err, None),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData(
"Expected at least 8 bytes of app data".into(),
));
}
let target_id = TargetIdWithApid::from_tc(&tc).expect("invalid tc format");
let unique_id = u32::from_be_bytes(tc.user_data()[0..4].try_into().unwrap());
if !self.request_handlers.contains_key(&target_id) {
self.psb
.common
.verification_handler
.borrow_mut()
.start_failure(
ecss_tc_and_token.token,
FailParams::new(Some(&time_stamp), &hk_err::UNKNOWN_TARGET_ID, None),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData(format!(
"Unknown target ID {target_id}"
)));
}
let send_request = |target: TargetIdWithApid, request: HkRequest| {
let sender = self.request_handlers.get(&target).unwrap();
sender
.send(RequestWithToken::new(
target,
Request::Hk(request),
ecss_tc_and_token.token,
))
.unwrap_or_else(|_| panic!("Sending HK request {request:?} failed"));
};
if subservice == hk::Subservice::TcEnableHkGeneration as u8 {
send_request(target_id, HkRequest::Enable(unique_id));
} else if subservice == hk::Subservice::TcDisableHkGeneration as u8 {
send_request(target_id, HkRequest::Disable(unique_id));
} else if subservice == hk::Subservice::TcGenerateOneShotHk as u8 {
send_request(target_id, HkRequest::OneShot(unique_id));
} else if subservice == hk::Subservice::TcModifyHkCollectionInterval as u8 {
if user_data.len() < 12 {
self.psb
.common
.verification_handler
.borrow_mut()
.start_failure(
ecss_tc_and_token.token,
FailParams::new(
Some(&time_stamp),
&hk_err::COLLECTION_INTERVAL_MISSING,
None,
),
)
.expect("Sending start failure TM failed");
return Err(PusPacketHandlingError::NotEnoughAppData(
"Collection interval missing".into(),
));
}
send_request(
target_id,
HkRequest::ModifyCollectionInterval(
unique_id,
CollectionIntervalFactor::from_be_bytes(user_data[8..12].try_into().unwrap()),
),
);
}
Ok(PusPacketHandlerResult::RequestHandled)
}
}
pub struct Pus3Wrapper<TcInMemConverter: EcssTcInMemConverter> {
pub(crate) pus_3_handler: PusService3HkHandler<
TcInMemConverter,
VerificationReporterWithSender,
ExampleHkRequestConverter,
GenericRequestRouter,
GenericRoutingErrorHandler<3>,
>,
pub(crate) pus_3_handler: PusService3HkHandler<TcInMemConverter>,
}
impl<TcInMemConverter: EcssTcInMemConverter> Pus3Wrapper<TcInMemConverter> {

View File

@ -1,15 +1,11 @@
use crate::tmtc::MpscStoreAndSendError;
use log::warn;
use satrs::pus::verification::{
FailParams, StdVerifReporterWithSender, VerificationReportingProvider,
};
use satrs::pus::{
EcssTcAndToken, GenericRoutingError, PusPacketHandlerResult, PusRoutingErrorHandler, TcInMemory,
};
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusServiceId;
use satrs::spacepackets::time::cds::TimeProvider;
use satrs::spacepackets::time::TimeWriter;
use satrs_core::pus::verification::{FailParams, StdVerifReporterWithSender};
use satrs_core::pus::{EcssTcAndToken, PusPacketHandlerResult, TcInMemory};
use satrs_core::spacepackets::ecss::tc::PusTcReader;
use satrs_core::spacepackets::ecss::PusServiceId;
use satrs_core::spacepackets::time::cds::TimeProvider;
use satrs_core::spacepackets::time::TimeWriter;
use satrs_example::config::{tmtc_err, CustomPusServiceId};
use std::sync::mpsc::Sender;
@ -82,7 +78,7 @@ impl PusReceiver {
self.stamp_helper.update_from_now();
let accepted_token = self
.verif_reporter
.acceptance_success(init_token, self.stamp_helper.stamp())
.acceptance_success(init_token, Some(self.stamp_helper.stamp()))
.expect("Acceptance success failure");
let service = PusServiceId::try_from(service);
match service {
@ -119,9 +115,9 @@ impl PusReceiver {
let result = self.verif_reporter.start_failure(
accepted_token,
FailParams::new(
self.stamp_helper.stamp(),
Some(self.stamp_helper.stamp()),
&tmtc_err::PUS_SERVICE_NOT_IMPLEMENTED,
&[standard_service as u8],
Some(&[standard_service as u8]),
),
);
if result.is_err() {
@ -143,9 +139,9 @@ impl PusReceiver {
.start_failure(
accepted_token,
FailParams::new(
self.stamp_helper.stamp(),
Some(self.stamp_helper.stamp()),
&tmtc_err::INVALID_PUS_SUBSERVICE,
&[e.number],
Some(&[e.number]),
),
)
.expect("Start failure verification failed")
@ -155,56 +151,3 @@ impl PusReceiver {
Ok(PusPacketHandlerResult::RequestHandled)
}
}
#[derive(Default)]
pub struct GenericRoutingErrorHandler<const SERVICE_ID: u8> {}
impl<const SERVICE_ID: u8> PusRoutingErrorHandler for GenericRoutingErrorHandler<SERVICE_ID> {
type Error = satrs::pus::GenericRoutingError;
fn handle_error(
&self,
target_id: satrs::TargetId,
token: satrs::pus::verification::VerificationToken<
satrs::pus::verification::TcStateAccepted,
>,
_tc: &PusTcReader,
error: Self::Error,
time_stamp: &[u8],
verif_reporter: &impl VerificationReportingProvider,
) {
warn!("Routing request for service {SERVICE_ID} failed: {error:?}");
match error {
GenericRoutingError::UnknownTargetId(id) => {
let mut fail_data: [u8; 8] = [0; 8];
fail_data.copy_from_slice(&id.to_be_bytes());
verif_reporter
.start_failure(
token,
FailParams::new(time_stamp, &tmtc_err::UNKNOWN_TARGET_ID, &fail_data),
)
.expect("Sending start failure failed");
}
GenericRoutingError::SendError(_) => {
let mut fail_data: [u8; 8] = [0; 8];
fail_data.copy_from_slice(&target_id.to_be_bytes());
verif_reporter
.start_failure(
token,
FailParams::new(time_stamp, &tmtc_err::ROUTING_ERROR, &fail_data),
)
.expect("Sending start failure failed");
}
GenericRoutingError::NotEnoughAppData { expected, found } => {
let mut context_info = (found as u32).to_be_bytes().to_vec();
context_info.extend_from_slice(&(expected as u32).to_be_bytes());
verif_reporter
.start_failure(
token,
FailParams::new(time_stamp, &tmtc_err::NOT_ENOUGH_APP_DATA, &context_info),
)
.expect("Sending start failure failed");
}
}
}
}

View File

@ -2,17 +2,17 @@ use std::sync::mpsc;
use std::time::Duration;
use log::{error, info, warn};
use satrs::pool::{PoolProvider, StaticMemoryPool, StoreAddr};
use satrs::pus::scheduler::{PusScheduler, TcInfo};
use satrs::pus::scheduler_srv::PusService11SchedHandler;
use satrs::pus::verification::VerificationReporterWithSender;
use satrs::pus::{
use satrs_core::pool::{PoolProvider, StaticMemoryPool, StoreAddr};
use satrs_core::pus::scheduler::{PusScheduler, TcInfo};
use satrs_core::pus::scheduler_srv::PusService11SchedHandler;
use satrs_core::pus::verification::VerificationReporterWithSender;
use satrs_core::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInSharedStoreConverter, EcssTcInVecConverter,
MpscTcReceiver, MpscTmAsVecSender, MpscTmInSharedPoolSender, PusPacketHandlerResult,
PusServiceHelper,
};
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs::ChannelId;
use satrs_core::tmtc::tm_helper::SharedTmPool;
use satrs_core::ChannelId;
use satrs_example::config::{TcReceiverId, TmSenderId, PUS_APID};
use crate::tmtc::PusTcSourceProviderSharedPool;
@ -52,8 +52,7 @@ impl TcReleaser for mpsc::Sender<Vec<u8>> {
}
pub struct Pus11Wrapper<TcInMemConverter: EcssTcInMemConverter> {
pub pus_11_handler:
PusService11SchedHandler<TcInMemConverter, VerificationReporterWithSender, PusScheduler>,
pub pus_11_handler: PusService11SchedHandler<TcInMemConverter, PusScheduler>,
pub sched_tc_pool: StaticMemoryPool,
pub releaser_buf: [u8; 4096],
pub tc_releaser: Box<dyn TcReleaser + Send>,

View File

@ -1,4 +1,4 @@
use satrs::pus::EcssTcInMemConverter;
use satrs_core::pus::EcssTcInMemConverter;
use super::{
action::Pus8Wrapper, event::Pus5Wrapper, hk::Pus3Wrapper, scheduler::Pus11Wrapper,

View File

@ -1,21 +1,19 @@
use log::{info, warn};
use satrs::params::Params;
use satrs::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs::pus::test::PusService17TestHandler;
use satrs::pus::verification::{
FailParams, VerificationReporterWithSender, VerificationReportingProvider,
};
use satrs::pus::{
use satrs_core::params::Params;
use satrs_core::pool::{SharedStaticMemoryPool, StoreAddr};
use satrs_core::pus::test::PusService17TestHandler;
use satrs_core::pus::verification::{FailParams, VerificationReporterWithSender};
use satrs_core::pus::{
EcssTcAndToken, EcssTcInMemConverter, EcssTcInVecConverter, MpscTcReceiver, MpscTmAsVecSender,
MpscTmInSharedPoolSender, PusPacketHandlerResult, PusServiceHelper,
};
use satrs::spacepackets::ecss::tc::PusTcReader;
use satrs::spacepackets::ecss::PusPacket;
use satrs::spacepackets::time::cds::TimeProvider;
use satrs::spacepackets::time::TimeWriter;
use satrs::tmtc::tm_helper::SharedTmPool;
use satrs::ChannelId;
use satrs::{events::EventU32, pus::EcssTcInSharedStoreConverter};
use satrs_core::spacepackets::ecss::tc::PusTcReader;
use satrs_core::spacepackets::ecss::PusPacket;
use satrs_core::spacepackets::time::cds::TimeProvider;
use satrs_core::spacepackets::time::TimeWriter;
use satrs_core::tmtc::tm_helper::SharedTmPool;
use satrs_core::ChannelId;
use satrs_core::{events::EventU32, pus::EcssTcInSharedStoreConverter};
use satrs_example::config::{tmtc_err, TcReceiverId, TmSenderId, PUS_APID, TEST_EVENT};
use std::sync::mpsc::{self, Sender};
@ -81,7 +79,7 @@ pub fn create_test_service_dynamic(
}
pub struct Service17CustomWrapper<TcInMemConverter: EcssTcInMemConverter> {
pub pus17_handler: PusService17TestHandler<TcInMemConverter, VerificationReporterWithSender>,
pub pus17_handler: PusService17TestHandler<TcInMemConverter>,
pub test_srv_event_sender: Sender<(EventU32, Option<Params>)>,
}
@ -127,13 +125,15 @@ impl<TcInMemConverter: EcssTcInMemConverter> Service17CustomWrapper<TcInMemConve
.service_helper
.common
.verification_handler
.start_success(token, &stamp_buf)
.get_mut()
.start_success(token, Some(&stamp_buf))
.expect("Error sending start success");
self.pus17_handler
.service_helper
.common
.verification_handler
.completion_success(start_token, &stamp_buf)
.get_mut()
.completion_success(start_token, Some(&stamp_buf))
.expect("Error sending completion success");
} else {
let fail_data = [tc.subservice()];
@ -141,12 +141,13 @@ impl<TcInMemConverter: EcssTcInMemConverter> Service17CustomWrapper<TcInMemConve
.service_helper
.common
.verification_handler
.get_mut()
.start_failure(
token,
FailParams::new(
&stamp_buf,
Some(&stamp_buf),
&tmtc_err::INVALID_PUS_SUBSERVICE,
&fail_data,
Some(&fail_data),
),
)
.expect("Sending start failure verification failed");

View File

@ -1,45 +0,0 @@
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone)]
pub enum GenericSendError {
RxDisconnected,
QueueFull(Option<u32>),
}
impl Display for GenericSendError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
GenericSendError::RxDisconnected => {
write!(f, "rx side has disconnected")
}
GenericSendError::QueueFull(max_cap) => {
write!(f, "queue with max capacity of {max_cap:?} is full")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericSendError {}
/// Generic error type for sending something via a message queue.
#[derive(Debug, Copy, Clone)]
pub enum GenericRecvError {
Empty,
TxDisconnected,
}
impl Display for GenericRecvError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::TxDisconnected => {
write!(f, "tx side has disconnected")
}
Self::Empty => {
write!(f, "nothing to receive")
}
}
}
}
#[cfg(feature = "std")]
impl Error for GenericRecvError {}

View File

@ -1,16 +1,15 @@
use std::collections::HashMap;
use std::sync::mpsc;
use derive_new::new;
use satrs::action::ActionRequest;
use satrs::hk::HkRequest;
use satrs::mode::ModeRequest;
use satrs::pus::action::PusActionRequestRouter;
use satrs::pus::hk::PusHkRequestRouter;
use satrs::pus::verification::{TcStateAccepted, VerificationToken};
use satrs::pus::GenericRoutingError;
use satrs::queue::GenericSendError;
use satrs::TargetId;
use satrs_core::hk::HkRequest;
use satrs_core::mode::ModeRequest;
use satrs_core::pus::verification::{TcStateAccepted, VerificationToken};
use satrs_example::TargetIdWithApid;
#[allow(dead_code)]
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum ActionRequest {
CmdWithU32Id((u32, Vec<u8>)),
CmdWithStringId((String, Vec<u8>)),
}
#[allow(dead_code)]
#[derive(Clone, Eq, PartialEq, Debug)]
@ -23,7 +22,7 @@ pub enum Request {
#[derive(Clone, Eq, PartialEq, Debug, new)]
pub struct TargetedRequest {
pub(crate) target_id: TargetId,
pub(crate) target_id_with_apid: TargetIdWithApid,
pub(crate) request: Request,
}
@ -35,7 +34,7 @@ pub struct RequestWithToken {
impl RequestWithToken {
pub fn new(
target_id: TargetId,
target_id: TargetIdWithApid,
request: Request,
token: VerificationToken<TcStateAccepted>,
) -> Self {
@ -45,50 +44,3 @@ impl RequestWithToken {
}
}
}
#[derive(Default, Clone)]
pub struct GenericRequestRouter(pub HashMap<TargetId, mpsc::Sender<RequestWithToken>>);
impl PusHkRequestRouter for GenericRequestRouter {
type Error = GenericRoutingError;
fn route(
&self,
target_id: TargetId,
hk_request: HkRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> {
if let Some(sender) = self.0.get(&target_id) {
sender
.send(RequestWithToken::new(
target_id,
Request::Hk(hk_request),
token,
))
.map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?;
}
Ok(())
}
}
impl PusActionRequestRouter for GenericRequestRouter {
type Error = GenericRoutingError;
fn route(
&self,
target_id: TargetId,
action_request: ActionRequest,
token: VerificationToken<TcStateAccepted>,
) -> Result<(), Self::Error> {
if let Some(sender) = self.0.get(&target_id) {
sender
.send(RequestWithToken::new(
target_id,
Request::Action(action_request),
token,
))
.map_err(|_| GenericRoutingError::SendError(GenericSendError::RxDisconnected))?;
}
Ok(())
}
}

View File

@ -4,7 +4,7 @@ use std::{
};
use log::{info, warn};
use satrs::{
use satrs_core::{
hal::std::tcp_server::{ServerConfig, TcpSpacepacketsServer},
spacepackets::PacketId,
tmtc::{CcsdsDistributor, CcsdsError, TmPacketSourceCore},

View File

@ -4,7 +4,7 @@ use std::{
};
use log::info;
use satrs::{
use satrs_core::{
pool::{PoolProvider, StoreAddr},
seq_count::{CcsdsSimpleSeqCountProvider, SequenceCountProviderCore},
spacepackets::{

Some files were not shown because too many files have changed in this diff Show More