mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 00:44:37 -06:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94b4457154 | ||
|
|
1f860de821 | ||
|
|
f3b191beff | ||
|
|
59e1baa9d8 | ||
|
|
43de02bafd | ||
|
|
6c80056596 | ||
|
|
a8fe137283 | ||
|
|
3a293cd705 | ||
|
|
e0afd3c3fb | ||
|
|
cddf38bb6f | ||
|
|
89a9e3247b | ||
|
|
a9e05045da | ||
|
|
9fbf5800ab | ||
|
|
08f43279da | ||
|
|
e8c3fea70c | ||
|
|
b9a7059667 | ||
|
|
aed0837c9c | ||
|
|
f5902583ce | ||
|
|
5baf4076ab | ||
|
|
ba95054a24 | ||
|
|
081a2f8654 | ||
|
|
027b658208 | ||
|
|
6d756f7431 | ||
|
|
8cbba542fc | ||
|
|
31b6488723 | ||
|
|
62dd7445db | ||
|
|
afb6f13853 | ||
|
|
6388f046e1 | ||
|
|
6a132c5df4 | ||
|
|
317ba3d7e0 | ||
|
|
402c0317b6 | ||
|
|
589871a957 | ||
|
|
cc2522b257 | ||
|
|
6af7bcd5a9 | ||
|
|
1c87278b6b | ||
|
|
a587677fae | ||
|
|
a46fccf54d | ||
|
|
2b65cc83cf | ||
|
|
90bac90821 | ||
|
|
ba5988bfcd | ||
|
|
d669ea2c5a | ||
|
|
48e4720419 | ||
|
|
b254e981a1 | ||
|
|
b1b613fd0c | ||
|
|
903677e97d | ||
|
|
d6f43b02ff | ||
|
|
d0e9508988 | ||
|
|
aa4517c69b | ||
|
|
edf5c85d09 | ||
|
|
695478d1a1 | ||
|
|
c51921aca3 | ||
|
|
cb9f3a982b | ||
|
|
033f504418 | ||
|
|
fdc582bc56 | ||
|
|
0d78cd407c | ||
|
|
bf12f48c27 | ||
|
|
3f25d8b535 | ||
|
|
c0b75fb06a | ||
|
|
6bdd79f290 | ||
|
|
38361bebd1 | ||
|
|
4261f9d815 | ||
|
|
c909a19a54 | ||
|
|
4705b6c989 | ||
|
|
626473fc4e | ||
|
|
1ff13ac93f | ||
|
|
a8328b95cf | ||
|
|
4afac326ba | ||
|
|
2fca863e4d | ||
|
|
0fc995fefd | ||
|
|
9c11872889 | ||
|
|
2ee27f82f3 | ||
|
|
9a7123c190 | ||
|
|
d385cb45ac | ||
|
|
2de91b0916 | ||
|
|
e1098018e2 | ||
|
|
e34c019c3b | ||
|
|
74dd80e2b7 | ||
|
|
7aa034c526 | ||
|
|
6c2698cea1 | ||
|
|
a4579d2fca | ||
|
|
896803ad20 | ||
|
|
35840d549d | ||
|
|
589ed5d437 | ||
|
|
e9d7f3ab66 | ||
|
|
20477ad9c8 | ||
|
|
498546eac9 | ||
|
|
c7048d0b9c | ||
|
|
0a945a55c9 | ||
|
|
7fbe13086a | ||
|
|
1786ffee01 | ||
|
|
9139148de2 | ||
|
|
43ed537106 | ||
|
|
157f0f4d23 | ||
|
|
3fa2ed9716 | ||
|
|
0e40726440 | ||
|
|
6433f694c7 | ||
|
|
8b1d627139 | ||
|
|
73ab9621e4 | ||
|
|
8ad0b830e8 | ||
|
|
9141f01919 | ||
|
|
b377be2a9f | ||
|
|
bae7d2573c | ||
|
|
40142d7d39 | ||
|
|
3422cfa117 | ||
|
|
01e3d70925 | ||
|
|
5bee1a1151 | ||
|
|
241e74ea1d | ||
|
|
b28709cbdb | ||
|
|
38f0c1f3b2 | ||
|
|
e21fa7b109 | ||
|
|
ade6cc5ff3 | ||
|
|
6190d9610b | ||
|
|
9871fd5608 | ||
|
|
942c9eb6ac | ||
|
|
965109bc50 | ||
|
|
054c51ad3e | ||
|
|
4f381110a1 | ||
|
|
31919e6931 | ||
|
|
ba65039fff | ||
|
|
c8e2d44877 | ||
|
|
82fff1abc8 | ||
|
|
fff0e0bf77 | ||
|
|
1ee55a0c90 | ||
|
|
aabfa56193 | ||
|
|
3e41e2052b | ||
|
|
4c0092751c | ||
|
|
d9ecb8b637 | ||
|
|
4429614ea4 | ||
|
|
9cb948ed5d | ||
|
|
0dbc4febbc | ||
|
|
bce0b5de79 | ||
|
|
d014f30a96 | ||
|
|
77f286f3ed | ||
|
|
10f2b49d3f | ||
|
|
8b9ac2197d | ||
|
|
b90345e9df | ||
|
|
976ad2b28e | ||
|
|
42c665f080 | ||
|
|
9ec8c559de | ||
|
|
287a0211eb | ||
|
|
d911fb903f | ||
|
|
373f81d0f3 | ||
|
|
513582f420 | ||
|
|
a20535ea42 | ||
|
|
475f323525 | ||
|
|
94f67e4e21 | ||
|
|
f4439a8cc9 | ||
|
|
bdade025d0 | ||
|
|
6915c93c10 | ||
|
|
7972392cf4 | ||
|
|
d6cd606e66 | ||
|
|
a070ee768a | ||
|
|
30a3902e3e | ||
|
|
2dea8abedf | ||
|
|
abb09e4a68 | ||
|
|
4e5bb71460 | ||
|
|
b72d866d19 | ||
|
|
4deda3d75f | ||
|
|
f38010abca | ||
|
|
89bccfa438 | ||
|
|
96f445f6ef | ||
|
|
ab16b2aabf | ||
|
|
be7e4ea098 | ||
|
|
21faffa6f8 | ||
|
|
dfaf496263 | ||
|
|
d460468b1b | ||
|
|
c69ff72110 | ||
|
|
e7840dee81 | ||
|
|
5ce0c5ada8 | ||
|
|
feb1418a0f | ||
|
|
ab58cf18fb | ||
|
|
02afd01545 | ||
|
|
60e7cb7993 | ||
|
|
bf7d9866cf | ||
|
|
29b9f2b4bf | ||
|
|
67be6defa0 | ||
|
|
e17d42a9ff | ||
|
|
578e61f1da | ||
|
|
26aad95ec0 |
@ -2,4 +2,5 @@
|
||||
BasedOnStyle: Chromium
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
AccessModifierOffset: -2
|
||||
AccessModifierOffset: -2
|
||||
InsertNewlineAtEOF: true
|
||||
|
||||
@ -6,6 +6,6 @@ Checks: >
|
||||
modernize-redundant-void-arg,
|
||||
modernize-use-bool-literals,
|
||||
modernize-use-nullptr,
|
||||
readability-braces-around-statements
|
||||
-clang-analyzer-security.insecureAPI.rand,
|
||||
readability-braces-around-statements,
|
||||
-clang-analyzer-security.insecureAPI.rand
|
||||
WarningsAsErrors: '*'
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
set timeout 180
|
||||
spawn fprime-bootstrap project
|
||||
expect -re {.*Project name.*}
|
||||
expect -re {.*Project repository name.*}
|
||||
send "my-project\r"
|
||||
expect -re {.*Project top-level namespace.*}
|
||||
send "MyProject\r"
|
||||
expect eof
|
||||
|
||||
@ -2,6 +2,8 @@ set timeout 60
|
||||
spawn fprime-util new --deployment
|
||||
expect -re {Deployment name.*}
|
||||
send "MyDeployment\r"
|
||||
expect -re {Deployment namespace.*}
|
||||
send "MyNamespace\r"
|
||||
expect -re {.*Select communication driver type}
|
||||
send "1\r"
|
||||
expect -re "Add .*MyDeployment.*"
|
||||
|
||||
52
.github/actions/spelling/expect.txt
vendored
52
.github/actions/spelling/expect.txt
vendored
@ -32,13 +32,19 @@ baremetal
|
||||
batchmode
|
||||
BDV
|
||||
bfree
|
||||
BHn
|
||||
bibtex
|
||||
Bies
|
||||
binaryfile
|
||||
BINDIR
|
||||
bitmaps
|
||||
bitshifts
|
||||
bitwidth
|
||||
bocchino
|
||||
boolt
|
||||
bsd
|
||||
BSDI
|
||||
bst
|
||||
BUFFERALLOCATE
|
||||
BUFFERALLOCATIONFAILED
|
||||
BUFFERGETOUT
|
||||
@ -58,6 +64,7 @@ Campuzano
|
||||
carg
|
||||
CBF
|
||||
CBLOCK
|
||||
CCACHE
|
||||
CCB
|
||||
CComponent
|
||||
ccsds
|
||||
@ -110,6 +117,7 @@ coravy
|
||||
coreutils
|
||||
Coverity
|
||||
CPHA
|
||||
CPIU
|
||||
cplusplus
|
||||
CPOL
|
||||
cppcheck
|
||||
@ -136,6 +144,7 @@ CYCLEOUT
|
||||
DATAPRODUCTS
|
||||
DATAPRODUCTSSUBTOPOLOGY
|
||||
DATAROOTDIR
|
||||
datasheet
|
||||
DDDTHH
|
||||
Debian
|
||||
deconstructor
|
||||
@ -177,6 +186,7 @@ DPWRITER
|
||||
DRAINBUFFERS
|
||||
drv
|
||||
drvtcpserversocket
|
||||
DSL
|
||||
dspal
|
||||
DSSC
|
||||
Dstate
|
||||
@ -189,6 +199,7 @@ ECLIPSEHELP
|
||||
EHAs
|
||||
eip
|
||||
Elts
|
||||
emptydir
|
||||
endcond
|
||||
endfunction
|
||||
endmacro
|
||||
@ -209,6 +220,7 @@ externalproject
|
||||
FAKELOGGER
|
||||
fbuild
|
||||
fdp
|
||||
featherm
|
||||
fecf
|
||||
FEEDNAME
|
||||
feq
|
||||
@ -222,17 +234,20 @@ FILEDOWNLINKCFG
|
||||
FILEHANDLING
|
||||
FILEHANDLINGSUBTOPOLOGY
|
||||
FILEID
|
||||
FILEMANAGERCONFIG
|
||||
FILEOPENERROR
|
||||
FILEWRITEERROR
|
||||
fio
|
||||
fle
|
||||
FNDELAY
|
||||
fne
|
||||
fontcolor
|
||||
FONTPATH
|
||||
foodoodie
|
||||
foodoodiehoo
|
||||
FPCONFIG
|
||||
fpext
|
||||
fpow
|
||||
fpp
|
||||
fppi
|
||||
fpptest
|
||||
@ -243,13 +258,12 @@ fptr
|
||||
fptrunc
|
||||
fputil
|
||||
fpy
|
||||
fpyc
|
||||
FPYSEQUENCER
|
||||
freeram
|
||||
Fregoso
|
||||
fres
|
||||
frsize
|
||||
fsblkcnt
|
||||
fsrc
|
||||
fsw
|
||||
FWCASSERT
|
||||
gcda
|
||||
@ -270,6 +284,7 @@ gpiochip
|
||||
gpioevent
|
||||
gpiohandle
|
||||
gpioline
|
||||
GPT
|
||||
Graphviz
|
||||
grayscales
|
||||
GROUNDINTERFACERULES
|
||||
@ -277,6 +292,7 @@ GSE
|
||||
gtags
|
||||
gtest
|
||||
gtimeout
|
||||
GTK
|
||||
Guire
|
||||
GVCID
|
||||
HACKSM
|
||||
@ -303,40 +319,41 @@ HTMLHELP
|
||||
ibd
|
||||
ieeetr
|
||||
ieq
|
||||
iext
|
||||
iflag
|
||||
ifstr
|
||||
ilhs
|
||||
imple
|
||||
inbool
|
||||
INCLUDEDIR
|
||||
ine
|
||||
initstate
|
||||
inkscape
|
||||
inorder
|
||||
installable
|
||||
intlimits
|
||||
inttypes
|
||||
INVALIDBUFFER
|
||||
INVALIDHEADER
|
||||
INVALIDHEADERHASH
|
||||
invis
|
||||
invisi
|
||||
ioc
|
||||
ioctl
|
||||
ipas
|
||||
IPCFG
|
||||
IPHELPER
|
||||
irhs
|
||||
isf
|
||||
isgreater
|
||||
isgreaterequal
|
||||
isless
|
||||
islessequal
|
||||
isr
|
||||
isrc
|
||||
isunordered
|
||||
itimerspec
|
||||
itr
|
||||
itrunc
|
||||
itval
|
||||
janamian
|
||||
jawest
|
||||
Jax
|
||||
jdk
|
||||
jdperez
|
||||
@ -367,12 +384,15 @@ LINEEVENT
|
||||
LINEHANDLE
|
||||
linelength
|
||||
lineoffset
|
||||
LISTDIRECTORY
|
||||
lld
|
||||
llu
|
||||
LOCALSTATEDIR
|
||||
LOGGERRULES
|
||||
LOGPACKET
|
||||
lseek
|
||||
LTK
|
||||
lvar
|
||||
LVL
|
||||
lxml
|
||||
MACROFILE
|
||||
@ -400,6 +420,7 @@ MMAPALLOCATOR
|
||||
MML
|
||||
modbus
|
||||
MOVEFILE
|
||||
Mpu
|
||||
msc
|
||||
mscfile
|
||||
mseconds
|
||||
@ -417,6 +438,10 @@ ncsl
|
||||
newtio
|
||||
nmsgs
|
||||
NOBLOCK
|
||||
NODELABEL
|
||||
noinline
|
||||
NOLINT
|
||||
NOLINTNEXTLINE
|
||||
noparent
|
||||
norecords
|
||||
NOSPEC
|
||||
@ -439,6 +464,7 @@ openmct
|
||||
openpyxl
|
||||
openssldir
|
||||
optarg
|
||||
optin
|
||||
optind
|
||||
orgslist
|
||||
ortega
|
||||
@ -472,6 +498,7 @@ patsubst
|
||||
pdflatex
|
||||
penv
|
||||
PERLMOD
|
||||
PHASERMEMBEROUT
|
||||
PINGENTRIES
|
||||
PINGSEND
|
||||
pkill
|
||||
@ -486,6 +513,7 @@ PORTOUT
|
||||
PORTSELECTOR
|
||||
ppandian
|
||||
pregen
|
||||
prescaler
|
||||
prioritization
|
||||
PRIORITYQUEUE
|
||||
prm
|
||||
@ -529,6 +557,7 @@ RATELIMITERTESTER
|
||||
rawtime
|
||||
RAWTIMETESTER
|
||||
RBF
|
||||
rbt
|
||||
RCHILD
|
||||
rcvd
|
||||
rdwr
|
||||
@ -558,6 +587,7 @@ sbom
|
||||
scid
|
||||
scm
|
||||
sdd
|
||||
sdiv
|
||||
searchdata
|
||||
SENDPARTIAL
|
||||
seqgen
|
||||
@ -565,10 +595,10 @@ serializables
|
||||
setaffinity
|
||||
setinheritsched
|
||||
SETLOGGING
|
||||
setname
|
||||
setprotocol
|
||||
setschedparam
|
||||
setschedpolicy
|
||||
setser
|
||||
setstacksize
|
||||
settime
|
||||
sev
|
||||
@ -580,11 +610,13 @@ SHAREDSTATEDIR
|
||||
SHELLCOMMAND
|
||||
showinitializer
|
||||
sideeffect
|
||||
siext
|
||||
Signedness
|
||||
Silveira
|
||||
sitofp
|
||||
sle
|
||||
sloc
|
||||
smod
|
||||
socio
|
||||
SOCKETHELPER
|
||||
SOCKETIPDRIVER
|
||||
@ -675,13 +707,15 @@ tparam
|
||||
TPP
|
||||
trinomials
|
||||
tts
|
||||
typedef'ed
|
||||
typedef
|
||||
typedef'ed
|
||||
uart
|
||||
udiv
|
||||
UDPSOCKET
|
||||
uge
|
||||
uitofp
|
||||
UML
|
||||
umod
|
||||
UNEXP
|
||||
unistd
|
||||
UNITTESTASSERT
|
||||
@ -708,9 +742,11 @@ wrs
|
||||
wxgui
|
||||
wxy
|
||||
Xapian
|
||||
XBee
|
||||
xdf
|
||||
xdffe
|
||||
xsltproc
|
||||
xxxx
|
||||
XXYY
|
||||
ziext
|
||||
zimri
|
||||
|
||||
13
.github/pull_request_template.md
vendored
13
.github/pull_request_template.md
vendored
@ -3,20 +3,25 @@
|
||||
|**_Related Issue(s)_**| |
|
||||
|**_Has Unit Tests (y/n)_**| |
|
||||
|**_Documentation Included (y/n)_**| |
|
||||
|**_Generative AI was used in this contribution (y/n)_**| |
|
||||
|
||||
---
|
||||
## Change Description
|
||||
|
||||
A description of the changes contained in the PR.
|
||||
<!-- A description of the changes contained in the PR. -->
|
||||
|
||||
## Rationale
|
||||
|
||||
A rationale for this change. e.g. fixes bug, or most projects need XYZ feature.
|
||||
<!-- A rationale for this change. e.g. fixes bug, or most projects need XYZ feature. -->
|
||||
|
||||
## Testing/Review Recommendations
|
||||
|
||||
Fill in testing procedures, specific items to focus on for review, or other info to help the team verify these changes are flight-quality.
|
||||
<!-- Fill in testing procedures, specific items to focus on for review, or other info to help the team verify these changes are flight-quality. -->
|
||||
|
||||
## Future Work
|
||||
|
||||
Note any additional work that will be done relating to this issue.
|
||||
<!-- Note any additional work that will be done relating to this issue. -->
|
||||
|
||||
## AI Usage (see [policy](https://github.com/nasa/fprime/blob/devel/AI_POLICY.md))
|
||||
|
||||
<!-- If AI was used, please describe how it was utilized (e.g., code generation, documentation, testing, debugging assistance, etc.). -->
|
||||
|
||||
4
.github/workflows/build-test-rhel8.yml
vendored
4
.github/workflows/build-test-rhel8.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
- uses: ./.github/actions/setup
|
||||
- name: Build Framework
|
||||
run: |
|
||||
fprime-util generate
|
||||
fprime-util generate -DFPRIME_ENABLE_JSON_MODEL_GENERATION=ON
|
||||
fprime-util build --all -j4
|
||||
|
||||
Ref:
|
||||
@ -59,7 +59,7 @@ jobs:
|
||||
- name: Build Ref
|
||||
run: |
|
||||
cd Ref
|
||||
fprime-util generate
|
||||
fprime-util generate -DFPRIME_ENABLE_JSON_MODEL_GENERATION=ON
|
||||
fprime-util build -j4
|
||||
|
||||
UTs:
|
||||
|
||||
3
.github/workflows/cppcheck-scan.yml
vendored
3
.github/workflows/cppcheck-scan.yml
vendored
@ -48,8 +48,9 @@ jobs:
|
||||
fprime-util generate -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
fprime-util build --all --jobs "$(nproc || printf '%s\n' 1)"
|
||||
|
||||
# Since our subtopologies have EXCLUDE_FROM_ALL, we need to explicitly build them
|
||||
# Add EXCLUDE_FROM_ALL targets as we need to explicitly build them
|
||||
fprime-util build --target Svc_Subtopologies --jobs "$(nproc || printf '%s\n' 1)"
|
||||
fprime-util build --target Svc_GenericHub --jobs "$(nproc || printf '%s\n' 1)"
|
||||
echo CPPCHECK_OPTS=--project="$GITHUB_WORKSPACE/build-fprime-automatic-native/compile_commands.json" >> $GITHUB_ENV
|
||||
|
||||
- name: Run cppcheck
|
||||
|
||||
@ -23,6 +23,7 @@ env:
|
||||
AARCH64_TOOLCHAIN_DIR: /tmp/aarch64-toolchain
|
||||
AARCH64_TOOLCHAIN_URL: https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz
|
||||
ARM_TOOLS_PATH: /tmp/aarch64-toolchain
|
||||
FPRIME_LOCATION: ./lib/fprime
|
||||
|
||||
jobs:
|
||||
get-branch:
|
||||
@ -46,11 +47,11 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
path: ./fprime
|
||||
path: ${{ env.FPRIME_LOCATION }}
|
||||
fetch-depth: 0
|
||||
- uses: ./fprime/.github/actions/setup
|
||||
- uses: ./lib/fprime/.github/actions/setup
|
||||
with:
|
||||
location: ./fprime
|
||||
location: ${{ env.FPRIME_LOCATION }}
|
||||
- name: "Download and Setup AArch64 Linux Toolchain"
|
||||
run: |
|
||||
mkdir -p ${AARCH64_TOOLCHAIN_DIR}
|
||||
@ -67,8 +68,8 @@ jobs:
|
||||
run: |
|
||||
mkdir -p aarch64-linux-artifacts
|
||||
cp -r ./build-artifacts aarch64-linux-artifacts
|
||||
cp -r Components/Led/test/int aarch64-linux-artifacts
|
||||
- name: 'Archive Build Artifacts'
|
||||
cp -r LedBlinker/Components/Led/test/int aarch64-linux-artifacts
|
||||
- name: "Archive Build Artifacts"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: aarch64-linux-artifacts
|
||||
@ -83,7 +84,7 @@ jobs:
|
||||
- name: "Checkout F´ Repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: 'requirements.txt'
|
||||
sparse-checkout: "requirements.txt"
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: "Setup environment"
|
||||
run: |
|
||||
@ -96,13 +97,14 @@ jobs:
|
||||
name: aarch64-linux-artifacts
|
||||
- name: Run Integration Tests
|
||||
run: |
|
||||
DEPLOYMENT=LedBlinker_LedBlinkerDeployment
|
||||
. venv/bin/activate
|
||||
mkdir -p ci-logs
|
||||
chmod +x ./build-artifacts/aarch64-linux/LedBlinker/bin/LedBlinker
|
||||
fprime-gds --ip-client -d ./build-artifacts/aarch64-linux/LedBlinker --logs ./ci-logs &
|
||||
chmod +x ./build-artifacts/aarch64-linux/${DEPLOYMENT}/bin/${DEPLOYMENT}
|
||||
fprime-gds --ip-client -d ./build-artifacts/aarch64-linux/${DEPLOYMENT} --logs ./ci-logs &
|
||||
sleep 10
|
||||
pytest --dictionary ./build-artifacts/aarch64-linux/LedBlinker/dict/LedBlinkerTopologyDictionary.json ./int/led_integration_tests.py
|
||||
- name: 'Archive logs'
|
||||
pytest --dictionary ./build-artifacts/aarch64-linux/${DEPLOYMENT}/dict/LedBlinkerDeploymentTopologyDictionary.json ./int/led_integration_tests.py
|
||||
- name: "Archive logs"
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
|
||||
3
.github/workflows/ext-build-hello-world.yml
vendored
3
.github/workflows/ext-build-hello-world.yml
vendored
@ -31,6 +31,7 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-project-builder.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-tutorial-hello-world
|
||||
build_location: HelloWorldDeployment
|
||||
fprime_location: ./lib/fprime
|
||||
build_location: Hello/HelloWorldDeployment
|
||||
run_unit_tests: false # no UTs in HelloWorld project
|
||||
target_ref: ${{ needs.get-branch.outputs.target-branch }}
|
||||
|
||||
3
.github/workflows/ext-build-led-blinker.yml
vendored
3
.github/workflows/ext-build-led-blinker.yml
vendored
@ -31,6 +31,7 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-project-builder.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-workshop-led-blinker
|
||||
build_location: LedBlinker
|
||||
fprime_location: lib/fprime
|
||||
build_location: LedBlinker/LedBlinkerDeployment
|
||||
run_unit_tests: true
|
||||
target_ref: ${{ needs.get-branch.outputs.target-branch }}
|
||||
|
||||
3
.github/workflows/ext-build-math-comp.yml
vendored
3
.github/workflows/ext-build-math-comp.yml
vendored
@ -31,6 +31,7 @@ jobs:
|
||||
uses: ./.github/workflows/reusable-project-builder.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-tutorial-math-component
|
||||
build_location: MathDeployment
|
||||
build_location: MathProject/MathDeployment
|
||||
fprime_location: ./lib/fprime
|
||||
run_unit_tests: true
|
||||
target_ref: ${{ needs.get-branch.outputs.target-branch }}
|
||||
|
||||
20
.github/workflows/ext-cookiecutters-test.yml
vendored
20
.github/workflows/ext-cookiecutters-test.yml
vendored
@ -73,41 +73,43 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
path: ./MyProject/lib/fprime
|
||||
path: ./my-project/lib/fprime
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Update dependencies and install fprime-tools"
|
||||
run: |
|
||||
cd MyProject
|
||||
cd my-project
|
||||
. fprime-venv/bin/activate
|
||||
pip install -U -r ./lib/fprime/requirements.txt
|
||||
pip install -U -r ./requirements.txt
|
||||
pip install git+https://github.com/nasa/fprime-tools@${{ needs.get-tools-branch.outputs.target-branch }}
|
||||
|
||||
- name: "Version Check"
|
||||
run: |
|
||||
cd MyProject
|
||||
cd my-project
|
||||
. fprime-venv/bin/activate
|
||||
fprime-util version-check
|
||||
|
||||
- name: "Test Generate and Build Project"
|
||||
run: |
|
||||
cd MyProject
|
||||
cd my-project
|
||||
. fprime-venv/bin/activate
|
||||
fprime-util generate
|
||||
fprime-util build -j4
|
||||
|
||||
- name: "Test New Deployment and Build"
|
||||
run: |
|
||||
cd MyProject
|
||||
cd my-project
|
||||
. fprime-venv/bin/activate
|
||||
expect ./lib/fprime/.github/actions/cookiecutter-check/deployment.expect
|
||||
cd MyProject
|
||||
expect ../lib/fprime/.github/actions/cookiecutter-check/deployment.expect
|
||||
cd MyDeployment
|
||||
fprime-util build -j4
|
||||
|
||||
- name: "Test New Component and Build"
|
||||
run: |
|
||||
cd MyProject
|
||||
cd my-project
|
||||
. fprime-venv/bin/activate
|
||||
expect ./lib/fprime/.github/actions/cookiecutter-check/component.expect
|
||||
cd MyProject
|
||||
expect ../lib/fprime/.github/actions/cookiecutter-check/component.expect
|
||||
cd MyComponent
|
||||
fprime-util build -j4
|
||||
|
||||
36
.github/workflows/ext-fprime-zephyr-reference-pico2.yml
vendored
Normal file
36
.github/workflows/ext-fprime-zephyr-reference-pico2.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Cross-compile https://github.com/fprime-community/fprime-zephyr-reference
|
||||
|
||||
name: "External Repo: Zephyr Reference (Pico 2)"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ devel, release/** ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ devel, release/** ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- '.github/actions/spelling/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
# Cancel in-progress runs if a newer run is started on a given PR
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ !contains(github.ref, 'devel') && !contains(github.ref, 'release/')}}
|
||||
jobs:
|
||||
get-branch:
|
||||
name: "Get target branch"
|
||||
uses: ./.github/workflows/reusable-get-pr-branch.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-zephyr-reference
|
||||
|
||||
build:
|
||||
name: "Zephyr Build"
|
||||
needs: get-branch
|
||||
uses: ./.github/workflows/reusable-project-ci.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-zephyr-reference
|
||||
target_ref: ${{ needs.get-branch.outputs.target-branch }}
|
||||
ci_config_file: ./lib/fprime-zephyr/ci/sample-configs/pico2.yml
|
||||
|
||||
|
||||
36
.github/workflows/ext-fprime-zephyr-reference-teensy41.yml
vendored
Normal file
36
.github/workflows/ext-fprime-zephyr-reference-teensy41.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Cross-compile https://github.com/fprime-community/fprime-zephyr-reference
|
||||
|
||||
name: "External Repo: Zephyr Reference (Teensy 4.1)"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ devel, release/** ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ devel, release/** ]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- '.github/actions/spelling/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
# Cancel in-progress runs if a newer run is started on a given PR
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ !contains(github.ref, 'devel') && !contains(github.ref, 'release/')}}
|
||||
jobs:
|
||||
get-branch:
|
||||
name: "Get target branch"
|
||||
uses: ./.github/workflows/reusable-get-pr-branch.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-zephyr-reference
|
||||
|
||||
build:
|
||||
name: "Zephyr Build"
|
||||
needs: get-branch
|
||||
uses: ./.github/workflows/reusable-project-ci.yml
|
||||
with:
|
||||
target_repository: fprime-community/fprime-zephyr-reference
|
||||
target_ref: ${{ needs.get-branch.outputs.target-branch }}
|
||||
ci_config_file: ./lib/fprime-zephyr/ci/sample-configs/teensy41.yml
|
||||
|
||||
|
||||
23
.github/workflows/ext-raspberry-led-blinker.yml
vendored
23
.github/workflows/ext-raspberry-led-blinker.yml
vendored
@ -21,6 +21,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
RPI_TOOLCHAIN_DIR: /tmp/rpi-toolchain
|
||||
FPRIME_LOCATION: ./lib/fprime
|
||||
|
||||
jobs:
|
||||
get-branch:
|
||||
@ -44,11 +45,11 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
path: ./fprime
|
||||
path: ${{ env.FPRIME_LOCATION }}
|
||||
fetch-depth: 0
|
||||
- uses: ./fprime/.github/actions/setup
|
||||
- uses: ./lib/fprime/.github/actions/setup
|
||||
with:
|
||||
location: ./fprime
|
||||
location: ${{ env.FPRIME_LOCATION }}
|
||||
- name: "Setup RPI Toolchain"
|
||||
uses: fprime-community/setup-rpi-sysroot@main
|
||||
- name: "Generate RPI Build Cache"
|
||||
@ -56,13 +57,14 @@ jobs:
|
||||
fprime-util generate raspberrypi
|
||||
- name: "Build RPI"
|
||||
run: |
|
||||
cd LedBlinker/LedBlinkerDeployment
|
||||
fprime-util build raspberrypi
|
||||
- name: "Prepare artifacts"
|
||||
run: |
|
||||
mkdir -p rpi-artifacts
|
||||
cp -r ./build-artifacts rpi-artifacts
|
||||
cp -r Components/Led/test/int rpi-artifacts
|
||||
- name: 'Archive Build Artifacts'
|
||||
cp -r LedBlinker/Components/Led/test/int rpi-artifacts
|
||||
- name: "Archive Build Artifacts"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: rpi-artifacts
|
||||
@ -77,7 +79,7 @@ jobs:
|
||||
- name: "Checkout F´ Repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: 'requirements.txt'
|
||||
sparse-checkout: "requirements.txt"
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: "Setup environment"
|
||||
run: |
|
||||
@ -90,13 +92,14 @@ jobs:
|
||||
name: rpi-artifacts
|
||||
- name: Run Integration Tests
|
||||
run: |
|
||||
DEPLOYMENT=LedBlinker_LedBlinkerDeployment
|
||||
. venv/bin/activate
|
||||
mkdir -p ci-logs
|
||||
chmod +x ./build-artifacts/raspberrypi/LedBlinker/bin/LedBlinker
|
||||
fprime-gds --ip-client -d ./build-artifacts/raspberrypi/LedBlinker --logs ./ci-logs &
|
||||
chmod +x ./build-artifacts/raspberrypi/${DEPLOYMENT}/bin/${DEPLOYMENT}
|
||||
fprime-gds --ip-client -d ./build-artifacts/raspberrypi/${DEPLOYMENT} --logs ./ci-logs &
|
||||
sleep 10
|
||||
pytest --dictionary ./build-artifacts/raspberrypi/LedBlinker/dict/LedBlinkerTopologyDictionary.json ./int/led_integration_tests.py
|
||||
- name: 'Archive logs'
|
||||
pytest --dictionary ./build-artifacts/raspberrypi/${DEPLOYMENT}/dict/LedBlinkerDeploymentTopologyDictionary.json ./int/led_integration_tests.py
|
||||
- name: "Archive logs"
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
|
||||
17
.github/workflows/format-check.yml
vendored
17
.github/workflows/format-check.yml
vendored
@ -29,12 +29,19 @@ jobs:
|
||||
- uses: ./.github/actions/setup
|
||||
- name: "Check C++ Formatting"
|
||||
env:
|
||||
# Svc is currently listing all but Svc/FpySequencer
|
||||
CHECKED_DIRS: >-
|
||||
Fw/Buffer
|
||||
Fw/Cmd
|
||||
Fw/Com
|
||||
Svc/Ccsds
|
||||
Svc/EventManager
|
||||
CFDP
|
||||
default
|
||||
Drv
|
||||
FppTestProject
|
||||
Fw
|
||||
Os
|
||||
Ref
|
||||
Svc
|
||||
TestUtils
|
||||
Utils
|
||||
|
||||
run: |
|
||||
fprime-util format --check --dirs $CHECKED_DIRS
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/fpp-tests.yml
vendored
2
.github/workflows/fpp-tests.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
- name: "Generate UT build cache"
|
||||
working-directory: ./FppTestProject
|
||||
run: |
|
||||
fprime-util generate --ut
|
||||
fprime-util generate --ut -DFPRIME_ENABLE_JSON_MODEL_GENERATION=ON
|
||||
shell: bash
|
||||
- name: "Build UTs"
|
||||
working-directory: ./FppTestProject/FppTest
|
||||
|
||||
2
.github/workflows/fpp-to-json.yml
vendored
2
.github/workflows/fpp-to-json.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
- name: "Generate Ref Deployment"
|
||||
working-directory: ./Ref
|
||||
run: |
|
||||
fprime-util generate
|
||||
fprime-util generate -DFPRIME_ENABLE_JSON_MODEL_GENERATION=ON
|
||||
shell: bash
|
||||
- name: "Run fpp-to-json on Ref topology"
|
||||
working-directory: ./Ref/Top
|
||||
|
||||
8
.github/workflows/pip-check.yml
vendored
8
.github/workflows/pip-check.yml
vendored
@ -6,10 +6,10 @@ name: Python Dependency Check
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'requirements.txt'
|
||||
- "requirements.txt"
|
||||
pull_request:
|
||||
paths:
|
||||
- 'requirements.txt'
|
||||
- "requirements.txt"
|
||||
# Cancel in-progress runs if a newer run is started on a given PR
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@ -21,8 +21,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
# macos-13 is the last Intel-family runner; macos-latest is ARM
|
||||
runner: [macos-13, macos-latest, ubuntu-22.04, ubuntu-latest]
|
||||
# Purposefully test on both ARM and Intel macOS (macos-latest is ARM)
|
||||
runner: [macos-15-intel, macos-latest, ubuntu-22.04, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
|
||||
10
.github/workflows/reusable-get-pr-branch.yml
vendored
10
.github/workflows/reusable-get-pr-branch.yml
vendored
@ -34,14 +34,16 @@ jobs:
|
||||
- name: "Get target branch"
|
||||
id: get_target_branch
|
||||
run: |
|
||||
BRANCH_NAME="${GITHUB_REF#refs/heads/}"
|
||||
echo "Looking for 'pr-${{ github.event.number }}'/'${BRANCH_NAME}'"
|
||||
response_code_pr=`curl -w '%{response_code}' https://api.github.com/repos/${{ inputs.target_repository }}/branches/pr-${{ github.event.number }} -o /dev/null`
|
||||
response_code_base=`curl -w '%{response_code}' https://api.github.com/repos/${{ inputs.target_repository }}/branches/${{ github.event.pull_request.base.ref }} -o /dev/null`
|
||||
response_code_branch=`curl -w '%{response_code}' https://api.github.com/repos/${{ inputs.target_repository }}/branches/${BRANCH_NAME} -o /dev/null`
|
||||
if [[ "${{ github.event_name }}" == "pull_request" && "$response_code_pr" == "200" ]]; then
|
||||
echo "TARGET_BRANCH=pr-${{ github.event.number }}" >> $GITHUB_OUTPUT
|
||||
echo "PR branch found, using pr-${{ github.event.number }}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request" && "$response_code_base" == "200" ]]; then
|
||||
echo "TARGET_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT
|
||||
echo "Base branch found, using ${{ github.event.pull_request.base.ref }}"
|
||||
elif [[ "$response_code_branch" == "200" ]]; then
|
||||
echo "TARGET_BRANCH=${BRANCH_NAME}" >> $GITHUB_OUTPUT
|
||||
echo "Base branch found, using ${BRANCH_NAME}"
|
||||
else
|
||||
echo "TARGET_BRANCH=${{ inputs.default_target_ref }}" >> $GITHUB_OUTPUT
|
||||
echo "PR branch not found, using ${{ inputs.default_target_ref }}"
|
||||
|
||||
85
.github/workflows/reusable-project-ci.yml
vendored
Normal file
85
.github/workflows/reusable-project-ci.yml
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
# This workflow is intended for reuse by other workflows and will not run directly (no triggers).
|
||||
# The behavior is to run the steps of fprime-ci.
|
||||
name: "F´ CI - Reusable Workflow"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
target_repository:
|
||||
description: "Additional external repository to checkout (<owner>/<repo>)"
|
||||
required: true
|
||||
type: string
|
||||
fprime_location:
|
||||
description: "Relative path from the external project root to its F´ submodule"
|
||||
required: false
|
||||
type: string
|
||||
default: "./lib/fprime"
|
||||
target_ref:
|
||||
description: "Branch on target to checkout"
|
||||
required: false
|
||||
type: string
|
||||
default: "devel"
|
||||
ci_config_file:
|
||||
required: true
|
||||
type: string
|
||||
run_unit_tests:
|
||||
description: "Run an additional job in parallel to run unit tests."
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
runs_on:
|
||||
description: "Platform to run on. Defaults to ubuntu-22.04"
|
||||
required: false
|
||||
type: string
|
||||
default: "ubuntu-22.04"
|
||||
runs_on_int:
|
||||
description: "Platform to run integration tests on. Defaults to: apple-ci"
|
||||
required: false
|
||||
type: string
|
||||
default: "apple-ci"
|
||||
runs_on_ut:
|
||||
description: "Platform to run UTs on. Defaults to ubuntu-22.04"
|
||||
required: false
|
||||
type: string
|
||||
default: "ubuntu-22.04"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs_on }}
|
||||
name: "Build"
|
||||
steps:
|
||||
- name: "Make Space"
|
||||
uses: nasa/fprime-actions/make-space@devel
|
||||
- name: "Set up target repository"
|
||||
uses: nasa/fprime-actions/external-repository-setup@devel
|
||||
with:
|
||||
target_repository: ${{ inputs.target_repository }}
|
||||
fprime_location: ${{ inputs.fprime_location }}
|
||||
target_ref: ${{ inputs.target_ref }}
|
||||
stage: build
|
||||
- name: "Build Binary"
|
||||
run: |
|
||||
CCACHE_DISABLE=1 fprime-ci -c ${{ inputs.ci_config_file }} --add-stage build
|
||||
- name: Archive Results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: archive.tar.gz
|
||||
path: ./archive.tar.gz
|
||||
integration-tests:
|
||||
needs: build
|
||||
name: "Integration Tests"
|
||||
runs-on: ${{ inputs.runs_on_int }}
|
||||
steps:
|
||||
- name: "Set up target repository"
|
||||
uses: nasa/fprime-actions/external-repository-setup@devel
|
||||
with:
|
||||
target_repository: ${{ inputs.target_repository }}
|
||||
fprime_location: ${{ inputs.fprime_location }}
|
||||
target_ref: ${{ inputs.target_ref }}
|
||||
stage: int
|
||||
- name: Pull Archive Results
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: archive.tar.gz
|
||||
- name: "Integration tests"
|
||||
run: |
|
||||
fprime-ci -c ${{ inputs.ci_config_file }} --skip-stage build
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,6 +26,7 @@ RemoteSystemsTempFiles
|
||||
*.stackdump
|
||||
Dict
|
||||
*.core
|
||||
*.swp
|
||||
|
||||
**/coverage/
|
||||
*.gcov
|
||||
|
||||
3
.nav.yml
3
.nav.yml
@ -38,6 +38,8 @@ nav:
|
||||
- Svc:
|
||||
- "Svc/**/docs/sdd.md"
|
||||
- Fw:
|
||||
- DataStructures:
|
||||
- "Fw/DataStructures/docs/**.md"
|
||||
- "Fw/**/docs/sdd.md"
|
||||
- Drv:
|
||||
- "Drv/**/docs/sdd.md"
|
||||
@ -47,3 +49,4 @@ nav:
|
||||
- "docs/reference/*.md"
|
||||
- Support: '../support'
|
||||
- Events: '../events'
|
||||
- News: "../news"
|
||||
|
||||
84
AI_POLICY.md
Normal file
84
AI_POLICY.md
Normal file
@ -0,0 +1,84 @@
|
||||
# F´ Generative AI Usage Guidelines
|
||||
|
||||
We're excited about the potential of generative AI to help make [F´](https://github.com/nasa/fprime) development more productive, enjoyable, and accessible! Whether you're using AI to write code, improve documentation, or learn about complex systems, we welcome the thoughtful use of these powerful tools in your F´ contributions.
|
||||
|
||||
This guide shares our community's approach to using generative AI effectively and responsibly. You'll find practical tips, best practices, and simple guidelines to help you get the most out of AI tools while maintaining the quality standards that make F´ great.
|
||||
|
||||
## Our Position on Generative AI
|
||||
|
||||
F´ embraces technological advancement and innovation. Generative AI tools can assist with:
|
||||
|
||||
- Code generation and refactoring
|
||||
- Documentation creation and improvement
|
||||
- Test case development
|
||||
- Debugging assistance
|
||||
- Design pattern suggestions
|
||||
- Learning and understanding our codebases
|
||||
|
||||
However, the use of generative AI must align with our commitment to high technical standards, quality, and the collaborative nature of open source development.
|
||||
|
||||
## Disclosure
|
||||
|
||||
To maintain transparency and enable effective code review, contributors **must disclose all generative AI usage**.
|
||||
This includes contributions in the forms of **Pull Requests**, **Issues** or **Discussions**.
|
||||
|
||||
### Pull Request Submissions for Contributors
|
||||
|
||||
1. **Fill-In the "AI Used (y/n)" table entry** in the pull request template disclosing whether Gen AI was used in the pull request
|
||||
2. **Provide details in the "AI Usage" section** describing how generative AI was utilized
|
||||
|
||||
### What to Disclose
|
||||
|
||||
Include information about:
|
||||
|
||||
- **Type of assistance**: Code generation, documentation, debugging, testing, refactoring, etc.
|
||||
- **Scope of usage**: Which files, functions, or sections were AI-assisted
|
||||
- **Tool(s) used**: Name of the AI system(s) employed (e.g., GitHub Copilot, ChatGPT, etc.)
|
||||
- **Level of modification**: Whether AI-generated content was used as-is, modified, or used as inspiration
|
||||
|
||||
|
||||
### What AI Cannot Replace
|
||||
|
||||
- **Domain expertise** in flight software and embedded systems
|
||||
- **Understanding of F Prime architecture** and design patterns
|
||||
- **Critical thinking** about system requirements and constraints
|
||||
- **Human judgment** on safety-critical decisions
|
||||
- **Community collaboration** and peer review processes
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Providing Guidelines to AI Tools
|
||||
|
||||
When working with generative AI, provide clear rules and context to improve code quality and consistency. For Example:
|
||||
|
||||
- **Reference F´ Style Guidelines**: Include the [F´ Style Guidelines](https://github.com/nasa/fprime/wiki/F%C2%B4-Style-Guidelines) in your prompts
|
||||
- **Enforce coding standards**: Instruct AI to avoid "magic numbers" and use descriptive variable names or comments
|
||||
- **Provide project context**: Share relevant F´ architectural patterns and component structures
|
||||
|
||||
### Quality and Responsibility
|
||||
|
||||
- **Review all AI-generated code** thoroughly before submission
|
||||
- **Verify necessity and relevance** - Remove verbose or unnecessary AI-generated content
|
||||
- **Be concise** - Edit AI output to be clear and to-the-point
|
||||
- **Ensure compliance** with F Prime coding standards and style guidelines
|
||||
- **Verify correctness** and test all AI-assisted implementations
|
||||
- **Maintain authorship responsibility** - you are accountable for all submitted code regardless of its origin
|
||||
|
||||
### Security
|
||||
|
||||
- **Be cautious with external dependencies** suggested by AI tools
|
||||
- **Validate security implications** of AI-generated code, especially for flight software
|
||||
|
||||
### Code Review Considerations
|
||||
|
||||
- **Provide context** to reviewers about AI usage to enable informed evaluation
|
||||
- **Be prepared to explain** AI-generated logic and design decisions
|
||||
- **Accept feedback gracefully** - AI-generated code is not exempt from revision requests
|
||||
- **Document complex AI-assisted algorithms** clearly for future maintainers
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you have questions about appropriate AI usage or need guidance on disclosure:
|
||||
|
||||
- Open a [Discussion](https://github.com/nasa/fprime/discussions) for community input
|
||||
- Contact the Community Managers for specific guidance
|
||||
@ -14,133 +14,83 @@
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
|
||||
static U32 min(const U32 a, const U32 b) {
|
||||
return (a < b) ? a : b;
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
namespace CFDP {
|
||||
|
||||
Checksum ::
|
||||
Checksum() : m_value(0)
|
||||
{
|
||||
Checksum ::Checksum() : m_value(0) {}
|
||||
|
||||
}
|
||||
Checksum ::Checksum(const U32 value) : m_value(value) {}
|
||||
|
||||
Checksum ::
|
||||
Checksum(const U32 value) : m_value(value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Checksum ::
|
||||
Checksum(const Checksum &original)
|
||||
{
|
||||
Checksum ::Checksum(const Checksum& original) {
|
||||
this->m_value = original.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
Checksum ::
|
||||
~Checksum()
|
||||
{
|
||||
Checksum ::~Checksum() {}
|
||||
|
||||
}
|
||||
|
||||
Checksum& Checksum ::
|
||||
operator=(const Checksum& checksum)
|
||||
{
|
||||
Checksum& Checksum ::operator=(const Checksum& checksum) {
|
||||
this->m_value = checksum.m_value;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
bool Checksum ::
|
||||
operator==(const Checksum& checksum) const
|
||||
{
|
||||
bool Checksum ::operator==(const Checksum& checksum) const {
|
||||
return this->m_value == checksum.m_value;
|
||||
}
|
||||
}
|
||||
|
||||
bool Checksum ::
|
||||
operator!=(const Checksum& checksum) const
|
||||
{
|
||||
return not (*this == checksum);
|
||||
}
|
||||
bool Checksum ::operator!=(const Checksum& checksum) const {
|
||||
return not(*this == checksum);
|
||||
}
|
||||
|
||||
U32 Checksum ::
|
||||
getValue() const
|
||||
{
|
||||
U32 Checksum ::getValue() const {
|
||||
return this->m_value;
|
||||
}
|
||||
}
|
||||
|
||||
void Checksum ::
|
||||
update(
|
||||
const U8 *const data,
|
||||
const U32 offset,
|
||||
const U32 length
|
||||
)
|
||||
{
|
||||
void Checksum ::update(const U8* const data, const U32 offset, const U32 length) {
|
||||
U32 index = 0;
|
||||
|
||||
// Add the first word unaligned if necessary
|
||||
const U32 offsetMod4 = offset % 4;
|
||||
if (offsetMod4 != 0) {
|
||||
const U8 wordLength = static_cast<U8>(min(length, 4 - offsetMod4));
|
||||
this->addWordUnaligned(
|
||||
&data[index],
|
||||
static_cast<U8>(offset + index),
|
||||
wordLength
|
||||
);
|
||||
index += wordLength;
|
||||
const U8 wordLength = static_cast<U8>(min(length, 4 - offsetMod4));
|
||||
this->addWordUnaligned(&data[index], static_cast<U8>(offset + index), wordLength);
|
||||
index += wordLength;
|
||||
}
|
||||
|
||||
// Add the middle words aligned
|
||||
for ( ; index + 4 <= length; index += 4) {
|
||||
addWordAligned(&data[index]);
|
||||
for (; index + 4 <= length; index += 4) {
|
||||
addWordAligned(&data[index]);
|
||||
}
|
||||
|
||||
// Add the last word unaligned if necessary
|
||||
if (index < length) {
|
||||
const U8 wordLength = static_cast<U8>(length - index);
|
||||
this->addWordUnaligned(
|
||||
&data[index],
|
||||
static_cast<U8>(offset + index),
|
||||
wordLength
|
||||
);
|
||||
const U8 wordLength = static_cast<U8>(length - index);
|
||||
this->addWordUnaligned(&data[index], static_cast<U8>(offset + index), wordLength);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Checksum ::
|
||||
addWordAligned(const U8 *const word)
|
||||
{
|
||||
void Checksum ::addWordAligned(const U8* const word) {
|
||||
for (U8 i = 0; i < 4; ++i) {
|
||||
addByteAtOffset(word[i], i);
|
||||
addByteAtOffset(word[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Checksum ::
|
||||
addWordUnaligned(
|
||||
const U8 *word,
|
||||
const U8 position,
|
||||
const U8 length
|
||||
)
|
||||
{
|
||||
void Checksum ::addWordUnaligned(const U8* word, const U8 position, const U8 length) {
|
||||
FW_ASSERT(length < 4);
|
||||
U8 offset = position % 4;
|
||||
for (U8 i = 0; i < length; ++i) {
|
||||
addByteAtOffset(word[i], offset);
|
||||
++offset;
|
||||
if (offset == 4) {
|
||||
offset = 0;
|
||||
}
|
||||
addByteAtOffset(word[i], offset);
|
||||
++offset;
|
||||
if (offset == 4) {
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Checksum ::
|
||||
addByteAtOffset(
|
||||
const U8 byte,
|
||||
const U8 offset
|
||||
)
|
||||
{
|
||||
FW_ASSERT(offset < 4);
|
||||
const U32 addend = static_cast<U32>(byte) << (8*(3-offset));
|
||||
this->m_value += addend;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Checksum ::addByteAtOffset(const U8 byte, const U8 offset) {
|
||||
FW_ASSERT(offset < 4);
|
||||
const U32 addend = static_cast<U32>(byte) << (8 * (3 - offset));
|
||||
this->m_value += addend;
|
||||
}
|
||||
|
||||
} // namespace CFDP
|
||||
|
||||
@ -17,130 +17,120 @@
|
||||
|
||||
namespace CFDP {
|
||||
|
||||
//! \class Checksum
|
||||
//! \brief Class representing a 32-bit checksum as mandated by the CCSDS File
|
||||
//! Delivery Protocol.
|
||||
//!
|
||||
//! This checksum is calculated by update of an existing 32-bit value
|
||||
//! with the "next" 32-bit string drawn from the file data. Beginning
|
||||
//! at the start of the file, a 4-byte window moves up the file by four
|
||||
//! bytes per update. The update itself replaces the existing checksum
|
||||
//! with the byte-wise sum of the existing checksum and the file data
|
||||
//! contained in the window. Overflows in the addition are permitted
|
||||
//! and the carry discarded.
|
||||
//!
|
||||
//! If an update is to be made beginning at an offset into the file
|
||||
//! which is not aligned to a 4-byte boundary, the window is treated
|
||||
//! as beginning at the last 4-byte boundary, but is left-zero-padded.
|
||||
//! Similarly, where the file data for an update ends on an unaligned
|
||||
//! byte, the window extends up to the next boundary and is
|
||||
//! right-zero-padded.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! For buffer 0xDE 0xAD 0xBE 0xEF 0xCA 0xFE and initial zero checksum:
|
||||
//!
|
||||
//! ------------------------------------ Update 1
|
||||
//! Window 0xDE 0xAD 0xBE 0xEF
|
||||
//! Checksum 0xDEADBEEF
|
||||
//!
|
||||
//! ------------------------------------ Update 2
|
||||
//! Window 0xCA 0xFE
|
||||
//! Checksum 0xDEADBEEF+
|
||||
//! 0xCAFE0000
|
||||
//! ----------
|
||||
//! 0xA8ABBEEF <- Final value
|
||||
class Checksum {
|
||||
//! \class Checksum
|
||||
//! \brief Class representing a 32-bit checksum as mandated by the CCSDS File
|
||||
//! Delivery Protocol.
|
||||
//!
|
||||
//! This checksum is calculated by update of an existing 32-bit value
|
||||
//! with the "next" 32-bit string drawn from the file data. Beginning
|
||||
//! at the start of the file, a 4-byte window moves up the file by four
|
||||
//! bytes per update. The update itself replaces the existing checksum
|
||||
//! with the byte-wise sum of the existing checksum and the file data
|
||||
//! contained in the window. Overflows in the addition are permitted
|
||||
//! and the carry discarded.
|
||||
//!
|
||||
//! If an update is to be made beginning at an offset into the file
|
||||
//! which is not aligned to a 4-byte boundary, the window is treated
|
||||
//! as beginning at the last 4-byte boundary, but is left-zero-padded.
|
||||
//! Similarly, where the file data for an update ends on an unaligned
|
||||
//! byte, the window extends up to the next boundary and is
|
||||
//! right-zero-padded.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! For buffer 0xDE 0xAD 0xBE 0xEF 0xCA 0xFE and initial zero checksum:
|
||||
//!
|
||||
//! ------------------------------------ Update 1
|
||||
//! Window 0xDE 0xAD 0xBE 0xEF
|
||||
//! Checksum 0xDEADBEEF
|
||||
//!
|
||||
//! ------------------------------------ Update 2
|
||||
//! Window 0xCA 0xFE
|
||||
//! Checksum 0xDEADBEEF+
|
||||
//! 0xCAFE0000
|
||||
//! ----------
|
||||
//! 0xA8ABBEEF <- Final value
|
||||
class Checksum {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Types
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Types
|
||||
// ----------------------------------------------------------------------
|
||||
//! Construct a fresh Checksum object.
|
||||
Checksum();
|
||||
|
||||
public:
|
||||
//! Construct a Checksum object and initialize it with a value.
|
||||
Checksum(const U32 value);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
//! Copy a Checksum object.
|
||||
Checksum(const Checksum& original);
|
||||
|
||||
//! Construct a fresh Checksum object.
|
||||
Checksum();
|
||||
//! Destroy a Checksum object.
|
||||
~Checksum();
|
||||
|
||||
//! Construct a Checksum object and initialize it with a value.
|
||||
Checksum(const U32 value);
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Public instance methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Copy a Checksum object.
|
||||
Checksum(const Checksum &original);
|
||||
//! Assign checksum to this.
|
||||
Checksum& operator=(const Checksum& checksum);
|
||||
|
||||
//! Destroy a Checksum object.
|
||||
~Checksum();
|
||||
//! Compare checksum and this for equality.
|
||||
bool operator==(const Checksum& checksum) const;
|
||||
|
||||
public:
|
||||
//! Compare checksum and this for inequality.
|
||||
bool operator!=(const Checksum& checksum) const;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Public instance methods
|
||||
// ----------------------------------------------------------------------
|
||||
//! Update the checksum value by accumulating words in the given data.
|
||||
//!
|
||||
//! \important The data and data-length passed to this method are specifically
|
||||
//! those over which the update is made, rather than the entire
|
||||
//! file. Typically, therefore, `data` will be a pointer to the
|
||||
//! byte given by the offset, e.g. `&file_buffer[offset]`.
|
||||
//!
|
||||
void update(const U8* const data, //!< Beginning of the data over which to update.
|
||||
const U32 offset, //!< Offset into the file at which the data begins.
|
||||
const U32 length //!< Length of the update data in bytes.
|
||||
);
|
||||
|
||||
//! Assign checksum to this.
|
||||
Checksum& operator=(const Checksum& checksum);
|
||||
//! Get the checksum value
|
||||
U32 getValue() const;
|
||||
|
||||
//! Compare checksum and this for equality.
|
||||
bool operator==(const Checksum& checksum) const;
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private instance methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Compare checksum and this for inequality.
|
||||
bool operator!=(const Checksum& checksum) const;
|
||||
//! Add a four-byte aligned word to the checksum value
|
||||
void addWordAligned(const U8* const word //! The word
|
||||
);
|
||||
|
||||
//! Update the checksum value by accumulating words in the given data.
|
||||
//!
|
||||
//! \important The data and data-length passed to this method are specifically
|
||||
//! those over which the update is made, rather than the entire
|
||||
//! file. Typically, therefore, `data` will be a pointer to the
|
||||
//! byte given by the offset, e.g. `&file_buffer[offset]`.
|
||||
//!
|
||||
void update(const U8* const data, //!< Beginning of the data over which to update.
|
||||
const U32 offset, //!< Offset into the file at which the data begins.
|
||||
const U32 length //!< Length of the update data in bytes.
|
||||
);
|
||||
//! Add a four-byte unaligned word to the checksum value
|
||||
void addWordUnaligned(const U8* const word, //! The word
|
||||
const U8 position, //! The position of the word relative to the start of the file
|
||||
const U8 length //! The number of valid bytes in the word
|
||||
);
|
||||
|
||||
//! Get the checksum value
|
||||
U32 getValue() const;
|
||||
//! Add byte to value at offset in word
|
||||
void addByteAtOffset(const U8 byte, //! The byte
|
||||
const U8 offset //! The offset
|
||||
);
|
||||
|
||||
private:
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Private instance methods
|
||||
// ----------------------------------------------------------------------
|
||||
//! The accumulated checksum value
|
||||
U32 m_value;
|
||||
};
|
||||
|
||||
//! Add a four-byte aligned word to the checksum value
|
||||
void addWordAligned(
|
||||
const U8 *const word //! The word
|
||||
);
|
||||
|
||||
//! Add a four-byte unaligned word to the checksum value
|
||||
void addWordUnaligned(
|
||||
const U8 *const word, //! The word
|
||||
const U8 position, //! The position of the word relative to the start of the file
|
||||
const U8 length //! The number of valid bytes in the word
|
||||
);
|
||||
|
||||
//! Add byte to value at offset in word
|
||||
void addByteAtOffset(
|
||||
const U8 byte, //! The byte
|
||||
const U8 offset //! The offset
|
||||
);
|
||||
|
||||
private:
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The accumulated checksum value
|
||||
U32 m_value;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace CFDP
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// ======================================================================
|
||||
// ======================================================================
|
||||
// \title CFDP/Checksum/GTest/Checksums.cpp
|
||||
// \author bocchino
|
||||
// \brief cpp file for CFDP Checksum gtest utilities
|
||||
@ -7,26 +7,21 @@
|
||||
// Copyright (C) 2016, California Institute of Technology.
|
||||
// ALL RIGHTS RESERVED. United States Government Sponsorship
|
||||
// acknowledged.
|
||||
//
|
||||
// ======================================================================
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "CFDP/Checksum/GTest/Checksums.hpp"
|
||||
|
||||
namespace CFDP {
|
||||
|
||||
namespace GTest {
|
||||
|
||||
void Checksums ::
|
||||
compare(
|
||||
const CFDP::Checksum& expected,
|
||||
const CFDP::Checksum& actual
|
||||
)
|
||||
{
|
||||
const U32 expectedValue = expected.getValue();
|
||||
const U32 actualValue = actual.getValue();
|
||||
ASSERT_EQ(expectedValue, actualValue);
|
||||
}
|
||||
|
||||
}
|
||||
namespace GTest {
|
||||
|
||||
void Checksums ::compare(const CFDP::Checksum& expected, const CFDP::Checksum& actual) {
|
||||
const U32 expectedValue = expected.getValue();
|
||||
const U32 actualValue = actual.getValue();
|
||||
ASSERT_EQ(expectedValue, actualValue);
|
||||
}
|
||||
|
||||
} // namespace GTest
|
||||
|
||||
} // namespace CFDP
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// ======================================================================
|
||||
// ======================================================================
|
||||
// \title CFDP/Checksum/GTest/Checksums.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for CFDP Checksum gtest utilities
|
||||
@ -7,8 +7,8 @@
|
||||
// Copyright (C) 2016 California Institute of Technology.
|
||||
// ALL RIGHTS RESERVED. United States Government Sponsorship
|
||||
// acknowledged.
|
||||
//
|
||||
// ======================================================================
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#ifndef GTest_CFDP_Checksums_HPP
|
||||
#define GTest_CFDP_Checksums_HPP
|
||||
@ -19,21 +19,20 @@
|
||||
|
||||
namespace CFDP {
|
||||
|
||||
namespace GTest {
|
||||
namespace GTest {
|
||||
|
||||
//! Utilities for testing Checksum operations
|
||||
//!
|
||||
namespace Checksums {
|
||||
//! Utilities for testing Checksum operations
|
||||
//!
|
||||
namespace Checksums {
|
||||
|
||||
void compare(
|
||||
const CFDP::Checksum& expected, //!< Expected value
|
||||
const CFDP::Checksum& actual //!< Actual value
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
void compare(const CFDP::Checksum& expected, //!< Expected value
|
||||
const CFDP::Checksum& actual //!< Actual value
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
} // namespace GTest
|
||||
|
||||
} // namespace CFDP
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// Main.cpp
|
||||
// Main.cpp
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
@ -8,49 +8,47 @@
|
||||
|
||||
using namespace CFDP;
|
||||
|
||||
const U8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||
const U8 data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
const U32 expectedValue =
|
||||
(data[0] << 3*8) + (data[1] << 2*8) + (data[2] << 1*8) + data[3] +
|
||||
(data[4] << 3*8) + (data[5] << 2*8) + (data[6] << 1*8) + data[7];
|
||||
const U32 expectedValue = (data[0] << 3 * 8) + (data[1] << 2 * 8) + (data[2] << 1 * 8) + data[3] + (data[4] << 3 * 8) +
|
||||
(data[5] << 2 * 8) + (data[6] << 1 * 8) + data[7];
|
||||
|
||||
TEST(Checksum, OnePacket) {
|
||||
Checksum checksum;
|
||||
checksum.update(data, 0, 8);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
Checksum checksum;
|
||||
checksum.update(data, 0, 8);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
}
|
||||
|
||||
TEST(Checksum, TwoPacketsAligned) {
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 4);
|
||||
checksum.update(&data[4], 4, 4);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 4);
|
||||
checksum.update(&data[4], 4, 4);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
}
|
||||
|
||||
TEST(Checksum, TwoPacketsUnaligned1) {
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 3);
|
||||
checksum.update(&data[3], 3, 5);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 3);
|
||||
checksum.update(&data[3], 3, 5);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
}
|
||||
|
||||
TEST(Checksum, TwoPacketsUnaligned2) {
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 5);
|
||||
checksum.update(&data[5], 5, 3);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 5);
|
||||
checksum.update(&data[5], 5, 3);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
}
|
||||
|
||||
TEST(Checksum, ThreePackets) {
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 2);
|
||||
checksum.update(&data[2], 2, 3);
|
||||
checksum.update(&data[5], 5, 3);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
Checksum checksum;
|
||||
checksum.update(&data[0], 0, 2);
|
||||
checksum.update(&data[2], 2, 3);
|
||||
checksum.update(&data[5], 5, 3);
|
||||
ASSERT_EQ(expectedValue, checksum.getValue());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
109
CONTRIBUTING.md
109
CONTRIBUTING.md
@ -16,20 +16,11 @@ Specific Ways to Contribute:
|
||||
- [Ask a Question or Suggest Improvements](https://github.com/nasa/fprime/discussions/new)
|
||||
- [Report a Bug or Mistake](https://github.com/nasa/fprime/issues/new/choose)
|
||||
- [Review Contributions](https://github.com/nasa/fprime/pulls)
|
||||
- Submit a Pull Request
|
||||
- Submit a Pull Request see: [Code Contribution Process](#cod-ontribution-process)
|
||||
- Contribute to Ongoing Discussions and Reviews
|
||||
|
||||
Feel free to contribute any way that suits your skills and enjoy.
|
||||
|
||||
|
||||
> **Note:** [F´ Autocoder Python](https://github.com/nasa/fprime/tree/master/Autocoders) is being actively replaced
|
||||
> by [FPP](https://github.com/fprime-community/fpp). Thus we will no longer accept changes to this code except for
|
||||
> security and critical bug fixes done in the most minimal fashion.
|
||||
>
|
||||
> We do love Python fixes, please consider contributing to
|
||||
> [fprime-tools](https://github.com/fprime-community/fprime-tools) or
|
||||
> [fprime-gds](https://github.com/fprime-community/fprime-gds)
|
||||
|
||||
## Where to Start
|
||||
|
||||
First, contributors should build some understanding of F´. Read through the documentation, try a tutorial, or run a
|
||||
@ -44,28 +35,13 @@ with an [easy first issue](https://github.com/nasa/fprime/issues?q=is%3Aissue+is
|
||||
When starting to modify F´ directly, ask questions, seek help, and be patient. Remember to review the project structure,
|
||||
development process, and helpful tips sections below.
|
||||
|
||||
## Project Structure
|
||||
## Code Contribution Process
|
||||
|
||||
The F´ project is designed as a base software [framework](https://github.com/nasa/fprime) with additional
|
||||
[packages](https://github.com/fprime-community) designed to extend the framework. This means that occasionally we may
|
||||
move contributions in or out of these packages.
|
||||
All code contributions to F´ begin with an issue. Whether you're fixing a bug, adding a feature, or improving documentation, please start by opening an issue describing your proposal. The Change Control Board (CCB) reviews and approves issues before work begins to ensure alignment with project goals and standards. Once approved, you can proceed with implementation and submit a pull request (PR).
|
||||
|
||||
Key packages include:
|
||||
If a PR is opened for work that does not correspond to an approved issue, the PR will be routed through the CCB process first—reviewed on a best-effort basis—and may be delayed or declined depending on CCB decisions.You can read more about how this process works in the [F´ Governance document](https://github.com/nasa/fprime/blob/devel/GOVERNANCE.md).
|
||||
|
||||
- [fpp](https://github.com/fprime-community/fpp): fpp development repository
|
||||
- [fprime-tools](https://github.com/fprime-community/fprime-tools): `fprime-util` development repository
|
||||
- [fprime-gds](https://github.com/fprime-community/fprime-gds): `fprime-gds` development repository
|
||||
|
||||
|
||||
### F´ Repository Structure
|
||||
|
||||
Contributors to the [fprime](https://github.com/nasa/fprime) repository should understand the following key folders:
|
||||
|
||||
- [docs/UsersGuide](https://github.com/nasa/fprime/tree/devel/docs/UsersGuide): add new documentation in this or a subfolder
|
||||
- [Fw](https://github.com/nasa/fprime/tree/devel/Fw): changes here will be reviewed carefully because this code is critical across F
|
||||
- [Ref](https://github.com/nasa/fprime/tree/devel/Ref): update and maintain the Reference application here
|
||||
|
||||
## Development Process
|
||||
### Development Process
|
||||
|
||||
F´ follows a standard git flow development model. Developers should start with a
|
||||
[fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) of one of the F´ repositories and then develop
|
||||
@ -84,6 +60,11 @@ git checkout -b <desired branch name>
|
||||
|
||||
Once a pull request has been submitted the following process will begin.
|
||||
|
||||
**Best practice: commit messages and PRs**
|
||||
|
||||
We recommend users to use an [imperative-style phrasing](https://cbea.ms/git-commit/#imperative) when writing commit messages. F´ uses the "Squash & Merge" strategy, meaning that all commits made on a PR branch will be combined into one squashed commit when merged into F´. The commit message for the squashed commit defaults to use the title of the Pull Request, so we do ask contributors to please follow the imperative-style phrasing for the title of their Pull Requests.
|
||||
When opening a Pull Request, please fill in the given template, and link to any relevant issue on the repository.
|
||||
|
||||
### Submission Review
|
||||
|
||||
The pull request changes will be reviewed by the team and community supporting F´. Often this means that a discussion on
|
||||
@ -121,13 +102,31 @@ The checks are configured to run on the `devel` branch of each external reposito
|
||||
|
||||
Maintainers will gladly help you in this process.
|
||||
|
||||
## Final Approval and Submission
|
||||
### Final Approval and Submission
|
||||
|
||||
Once all corrections have been made, automated checks are passing, and a maintainer has given final approval, it is time
|
||||
to contribute the submission. A maintainer will handle this final step and once complete changes should appear in the
|
||||
`devel` branch. You can help this process by submitting any deferred or future work items as issues using the links
|
||||
above.
|
||||
|
||||
## Project Structure
|
||||
|
||||
The F´ project is designed as a base software [framework](https://github.com/nasa/fprime) with additional
|
||||
[packages](https://github.com/fprime-community) designed to extend the framework. This means that occasionally we may
|
||||
move contributions in or out of these packages.
|
||||
|
||||
Key packages include:
|
||||
|
||||
- [fpp](https://github.com/fprime-community/fpp): fpp development repository
|
||||
- [fprime-tools](https://github.com/fprime-community/fprime-tools): `fprime-util` development repository
|
||||
- [fprime-gds](https://github.com/fprime-community/fprime-gds): `fprime-gds` development repository
|
||||
|
||||
|
||||
### F´ Repository Structure
|
||||
|
||||
Contributors to the [fprime](https://github.com/nasa/fprime) repository should refer to the following guide to understand the repository structure: [A Tour of the Source Tree](docs/user-manual/overview/source-tree.md)
|
||||
|
||||
|
||||
## Helpful Tips
|
||||
|
||||
This section will describe some helpful tips for contributing to F´.
|
||||
@ -141,42 +140,54 @@ changes across many files.
|
||||
Keep in mind that editors that fix whitespace automatically can cause many small changes. Even with advanced GitHub
|
||||
tools this can increase the effort required to review a submission. Be careful with the changes you are submitting.
|
||||
|
||||
## Run Tests
|
||||
### Run Tests
|
||||
|
||||
The automatic checking system will run all our unit tests and integration tests across several systems. However, this
|
||||
process will take time. Try to run the unit tests locally during development before submitting a PR and use the
|
||||
automatic checks as a safety net.
|
||||
|
||||
Building and running the tests has the same Python virtual environment requirements as developing an F´ project, which
|
||||
is usually set up by fprime-bootstrap. Steps to set up the environment outside a project are included below.
|
||||
|
||||
The tests can be run using the following commands:
|
||||
|
||||
```bash
|
||||
# Go into the fprime directory
|
||||
cp MY_FPRIME_DIRECTORY
|
||||
cd MY_FPRIME_DIRECTORY
|
||||
|
||||
# Run CI tests on fprime
|
||||
./ci/tests/Framework.bash
|
||||
# Set up and activate a Python virtual environment, if none already:
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
|
||||
# Run CI tests on the reference application
|
||||
./ci/tests/Ref.bash
|
||||
# Make sure Python packages from ./requirements.txt are installed and up-to-date:
|
||||
pip install -Ur requirements.txt
|
||||
|
||||
# Initialize googletest submodule:
|
||||
git submodule update --init --recursive
|
||||
|
||||
# Run the static analyzer with the basic configuration
|
||||
# Purge unit test directory
|
||||
fprime-util purge
|
||||
# Generate the build files for clang-tidy. Make sure clang-tidy is installed.
|
||||
fprime-util generate --ut -DCMAKE_CXX_CLANG_TIDY=clang-tidy-12
|
||||
# Generate the build files. Using clang-tidy is optional, but recommended to match the CI checks.
|
||||
# On macOS, expect a CMake Warning 'Leak sanitizer is not supported on macOS in cmake/sanitizers.cmake'
|
||||
fprime-util generate --ut -DCMAKE_CXX_CLANG_TIDY=clang-tidy
|
||||
# Build fprime with the static analyzer
|
||||
fprime-util build --all --ut -j16
|
||||
fprime-util build --all --ut
|
||||
|
||||
# Run the static analyzer with additional flight code checks
|
||||
# Purge release directory
|
||||
fprime-util purge
|
||||
# Generate the build files for clang-tidy. Make sure clang-tidy is installed.
|
||||
fprime-util generate -DCMAKE_CXX_CLANG_TIDY="clang-tidy-12;--config-file=$PWD/release.clang-tidy"
|
||||
# Build fprime with the static analyzer
|
||||
fprime-util build --all -j16
|
||||
# Run Unit Tests
|
||||
fprime-util check --all
|
||||
```
|
||||
|
||||
## Development with modified FPP version
|
||||
### Code formatting
|
||||
|
||||
The F´ repository enforces formatting with `clang-format`. Most IDEs offer tools to format on demand or auto-format on "Save". To run formatting yourself, `fprime-util` provides a quick way to format all files that have been modified since you branched off of `devel`:
|
||||
|
||||
```bash
|
||||
git diff --name-only devel...HEAD | fprime-util format --stdin
|
||||
```
|
||||
|
||||
|
||||
### Development with modified FPP version
|
||||
|
||||
In case FPP needs to be locally changed, first uninstall all `fprime-fpp-*` `pip` packages, and install FPP
|
||||
using the procedure mentioned in the [FPP readme](https://github.com/nasa/fpp/blob/main/compiler/README.adoc).
|
||||
@ -190,5 +201,5 @@ cp MY_FPRIME_DIRECTORY
|
||||
# Generate the build files without checking the FPP version
|
||||
fprime-util generate -DFPRIME_SKIP_TOOLS_VERSION_CHECK=1
|
||||
# Build the project
|
||||
fprime-util build -j4
|
||||
```
|
||||
fprime-util build
|
||||
```
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
// ======================================================================
|
||||
// \title AsyncByteStreamBufferAdapter.cpp
|
||||
// \author bocchino
|
||||
// \brief cpp file for AsyncByteStreamBufferAdapter component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#include "Drv/AsyncByteStreamBufferAdapter/AsyncByteStreamBufferAdapter.hpp"
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Component construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
AsyncByteStreamBufferAdapter::AsyncByteStreamBufferAdapter(const char* const compName)
|
||||
: AsyncByteStreamBufferAdapterComponentBase(compName) {}
|
||||
|
||||
AsyncByteStreamBufferAdapter::~AsyncByteStreamBufferAdapter() {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void AsyncByteStreamBufferAdapter::bufferIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
// TODO: If m_driverIsReady then send fwBuffer on toByteStreamDriver_out
|
||||
// TODO: Otherwise
|
||||
// TODO: Log the error
|
||||
// TODO: Send fwBuffer on bufferInReturn_out
|
||||
}
|
||||
|
||||
void AsyncByteStreamBufferAdapter::bufferOutReturn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
// TODO: Send fwBuffer on fromByteStreamDriverReturn_out
|
||||
}
|
||||
|
||||
void AsyncByteStreamBufferAdapter::byteStreamDriverReady_handler(FwIndexType portNum) {
|
||||
this->m_driverIsReady = true;
|
||||
}
|
||||
|
||||
void AsyncByteStreamBufferAdapter::fromByteStreamDriver_handler(FwIndexType portNum,
|
||||
Fw::Buffer& buffer,
|
||||
const Drv::ByteStreamStatus& status) {
|
||||
// TODO: If the status is OK, then send buffer on toByteStreamDriver_out
|
||||
// TODO: Otherwise log the error and send buffer on fromByteStreamDriverReturn_out
|
||||
}
|
||||
|
||||
void AsyncByteStreamBufferAdapter::toByteStreamDriverReturn_handler(FwIndexType portNum,
|
||||
Fw::Buffer& buffer,
|
||||
const Drv::ByteStreamStatus& status) {
|
||||
// TODO: Send fwBuffer on bufferInReturn_out
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
@ -0,0 +1,33 @@
|
||||
module Drv {
|
||||
|
||||
@ A passive component for mediating between the AsyncByteStreamDriver
|
||||
@ interface and the PassiveBufferDriver interface
|
||||
@
|
||||
@ Sample topology:
|
||||
@
|
||||
@ -----------------------------------------------------------
|
||||
@ | |
|
||||
@ | AsyncByteStreamDriver <--> AsyncByteStreamBufferAdapter | <--> PassiveBufferDriverClient
|
||||
@ | |
|
||||
@ -----------------------------------------------------------
|
||||
@
|
||||
@ The two components in the box function together as a PassiveBufferDriver:
|
||||
@
|
||||
@ -------------------------------------------------
|
||||
@ | |
|
||||
@ | PassiveBufferDriver | <--> PassiveBufferDriverClient
|
||||
@ | |
|
||||
@ -------------------------------------------------
|
||||
@
|
||||
passive component AsyncByteStreamBufferAdapter {
|
||||
|
||||
@ AsyncByteStreamBufferAdapter is a passive client of the
|
||||
@ AsyncByteStreamDriver interface
|
||||
import PassiveAsyncByteStreamDriverClient
|
||||
|
||||
@ AsyncByteStreamBufferAdapter is a PassiveBufferDriver
|
||||
import PassiveBufferDriver
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
// ======================================================================
|
||||
// \title AsyncByteStreamBufferAdapter.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for AsyncByteStreamBufferAdapter component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#ifndef Drv_AsyncByteStreamBufferAdapter_HPP
|
||||
#define Drv_AsyncByteStreamBufferAdapter_HPP
|
||||
|
||||
#include "Drv/AsyncByteStreamBufferAdapter/AsyncByteStreamBufferAdapterComponentAc.hpp"
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class AsyncByteStreamBufferAdapter final : public AsyncByteStreamBufferAdapterComponentBase {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Component construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Construct AsyncByteStreamBufferAdapter object
|
||||
AsyncByteStreamBufferAdapter(const char* const compName //!< The component name
|
||||
);
|
||||
|
||||
//! Destroy AsyncByteStreamBufferAdapter object
|
||||
~AsyncByteStreamBufferAdapter();
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Handler implementation for bufferIn
|
||||
//!
|
||||
//! Port for receiving buffers
|
||||
void bufferIn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
|
||||
//! Handler implementation for bufferOutReturn
|
||||
//!
|
||||
//! Port for receiving buffers sent on bufferOut and then returned
|
||||
void bufferOutReturn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
|
||||
//! Handler implementation for byteStreamDriver
|
||||
//!
|
||||
//! Port for receiving ready signals from the driver
|
||||
//! Sample connection: byteStreamDriver.ready -> byteStreamDriverClient.byteStreamReady
|
||||
void byteStreamDriverReady_handler(FwIndexType portNum //!< The port number
|
||||
) override;
|
||||
|
||||
//! Handler implementation for fromByteStreamDriver
|
||||
//!
|
||||
//! Port for receiving data from the driver
|
||||
//! Sample connection: byteStreamDriver.$recv -> byteStreamDriverClient.fromDriver
|
||||
void fromByteStreamDriver_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& buffer,
|
||||
const Drv::ByteStreamStatus& status) override;
|
||||
|
||||
//! Handler implementation for toByteStreamDriverReturn
|
||||
//!
|
||||
//! Port for receiving buffers sent on toByteStreamDriver and then returned
|
||||
//! Sample connection: driver.sendReturnOut -> client.toByteStreamDriverReturn
|
||||
void toByteStreamDriverReturn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& buffer,
|
||||
const Drv::ByteStreamStatus& status) override;
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Whether the driver is ready
|
||||
bool m_driverIsReady = false;
|
||||
};
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
#endif
|
||||
10
Drv/AsyncByteStreamBufferAdapter/CMakeLists.txt
Normal file
10
Drv/AsyncByteStreamBufferAdapter/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/AsyncByteStreamBufferAdapter.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/AsyncByteStreamBufferAdapter.cpp"
|
||||
)
|
||||
|
||||
set(MOD_DEPS
|
||||
"Fw/Logger"
|
||||
)
|
||||
|
||||
register_fprime_module()
|
||||
47
Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.cpp
Normal file
47
Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
// ======================================================================
|
||||
// \title ByteStreamBufferAdapter.cpp
|
||||
// \author bocchino
|
||||
// \brief cpp file for ByteStreamBufferAdapter component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#include "Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.hpp"
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Component construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
ByteStreamBufferAdapter::ByteStreamBufferAdapter(const char* const compName)
|
||||
: ByteStreamBufferAdapterComponentBase(compName) {}
|
||||
|
||||
ByteStreamBufferAdapter::~ByteStreamBufferAdapter() {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void ByteStreamBufferAdapter::bufferIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
// TODO: If m_driverIsReady then
|
||||
// TODO: Send fwBuffer on toByteStreamDriver_out
|
||||
// TODO: Check the return status. If there is an error, then log it to the Logger.
|
||||
// TODO: Otherwise log the error
|
||||
// TODO: Send fwBuffer on bufferInReturn_out
|
||||
}
|
||||
|
||||
void ByteStreamBufferAdapter::bufferOutReturn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
// TODO: Send fwBuffer on fromByteStreamDriverReturn_out
|
||||
}
|
||||
|
||||
void ByteStreamBufferAdapter::fromByteStreamDriver_handler(FwIndexType portNum,
|
||||
Fw::Buffer& buffer,
|
||||
const Drv::ByteStreamStatus& status) {
|
||||
// TODO: If the status is OK, then send buffer on toByteStreamDriver_out
|
||||
// TODO: Otherwise log the error and send buffer on fromByteStreamDriverReturn_out
|
||||
}
|
||||
|
||||
void ByteStreamBufferAdapter::byteStreamDriverReady_handler(FwIndexType portNum) {
|
||||
this->m_driverIsReady = true;
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
33
Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.fpp
Normal file
33
Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.fpp
Normal file
@ -0,0 +1,33 @@
|
||||
module Drv {
|
||||
|
||||
@ A passive component for mediating between the ByteStreamDriver
|
||||
@ interface and the PassiveBufferDriver interface
|
||||
@
|
||||
@ Sample topology:
|
||||
@
|
||||
@ -------------------------------------------------
|
||||
@ | |
|
||||
@ | ByteStreamDriver <--> ByteStreamBufferAdapter | <--> PassiveBufferDriverClient
|
||||
@ | |
|
||||
@ -------------------------------------------------
|
||||
@
|
||||
@ The two components in the box function together as a PassiveBufferDriver:
|
||||
@
|
||||
@ -------------------------------------------------
|
||||
@ | |
|
||||
@ | PassiveBufferDriver | <--> PassiveBufferDriverClient
|
||||
@ | |
|
||||
@ -------------------------------------------------
|
||||
@
|
||||
passive component ByteStreamBufferAdapter {
|
||||
|
||||
@ ByteStreamBufferAdapter is a passive client of the ByteStreamDriver
|
||||
@ interface
|
||||
import PassiveByteStreamDriverClient
|
||||
|
||||
@ ByteStreamBufferAdapter is a PassiveBufferDriver
|
||||
import PassiveBufferDriver
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
72
Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.hpp
Normal file
72
Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapter.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
// ======================================================================
|
||||
// \title ByteStreamBufferAdapter.hpp
|
||||
// \author bocchino
|
||||
// \brief hpp file for ByteStreamBufferAdapter component implementation class
|
||||
// ======================================================================
|
||||
|
||||
#ifndef Drv_ByteStreamBufferAdapter_HPP
|
||||
#define Drv_ByteStreamBufferAdapter_HPP
|
||||
|
||||
#include "Drv/ByteStreamBufferAdapter/ByteStreamBufferAdapterComponentAc.hpp"
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class ByteStreamBufferAdapter final : public ByteStreamBufferAdapterComponentBase {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Component construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Construct ByteStreamBufferAdapter object
|
||||
ByteStreamBufferAdapter(const char* const compName //!< The component name
|
||||
);
|
||||
|
||||
//! Destroy ByteStreamBufferAdapter object
|
||||
~ByteStreamBufferAdapter();
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Handler implementation for bufferIn
|
||||
//!
|
||||
//! Port for receiving buffers
|
||||
void bufferIn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
|
||||
//! Handler implementation for bufferOutReturn
|
||||
//!
|
||||
//! Port for receiving buffers sent on bufferOut and then returned
|
||||
void bufferOutReturn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
|
||||
//! Handler implementation for byteStreamIn
|
||||
//!
|
||||
//! Port for receiving data from the driver
|
||||
//! Sample connection: byteStreamDriver.$recv -> byteStreamDriverClient.byteStreamIn
|
||||
void fromByteStreamDriver_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& buffer,
|
||||
const Drv::ByteStreamStatus& status) override;
|
||||
|
||||
//! Handler implementation for byteStreamReady
|
||||
//!
|
||||
//! Port for receiving ready signals from the driver
|
||||
//! Sample connection: byteStreamDriver.ready -> byteStreamDriverClient.byteStreamDriverReady
|
||||
void byteStreamDriverReady_handler(FwIndexType portNum //!< The port number
|
||||
) override;
|
||||
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Private member variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Whether the driver is ready
|
||||
bool m_driverIsReady = false;
|
||||
};
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
#endif
|
||||
10
Drv/ByteStreamBufferAdapter/CMakeLists.txt
Normal file
10
Drv/ByteStreamBufferAdapter/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(SOURCE_FILES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ByteStreamBufferAdapter.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ByteStreamBufferAdapter.cpp"
|
||||
)
|
||||
|
||||
set(MOD_DEPS
|
||||
"Fw/Logger"
|
||||
)
|
||||
|
||||
register_fprime_module()
|
||||
@ -1,7 +1,7 @@
|
||||
module Drv {
|
||||
|
||||
@ Status returned by the send call
|
||||
enum ByteStreamStatus {
|
||||
enum ByteStreamStatus : U8 {
|
||||
OP_OK @< Operation worked as expected
|
||||
SEND_RETRY @< Data send should be retried
|
||||
RECV_NO_DATA @< Receive worked, but there was no data
|
||||
@ -10,12 +10,17 @@ module Drv {
|
||||
|
||||
@ Port to exchange buffer and status with the ByteStreamDriver model
|
||||
@ This port is used for receiving data from the driver as well as on
|
||||
@ callback of a send call
|
||||
@ callback of an asynchronous send call
|
||||
port ByteStreamData(
|
||||
ref buffer: Fw.Buffer,
|
||||
status: ByteStreamStatus
|
||||
)
|
||||
|
||||
@ Synchronous only - Send data out through the byte stream
|
||||
port ByteStreamSend(
|
||||
ref sendBuffer: Fw.Buffer @< Data to send
|
||||
) -> ByteStreamStatus
|
||||
|
||||
@ Signal indicating the driver is ready to send and received data
|
||||
port ByteStreamReady()
|
||||
|
||||
|
||||
@ -5,10 +5,15 @@ The outgoing stream is represented by the input `send` port; other components ca
|
||||
|
||||
## Design
|
||||
|
||||
There are two versions for the ByteStreamDriver, a synchronous version (`Drv.ByteStreamDriver`) and an asynchronous version (`Drv.AsyncByteStreamDriver`). In the synchronous version, the (guarded) `send` port blocks and returns status. In the asynchronous version, the (async) `send` port calls back on the `sendReturnOut` port to return status and buffer ownership.
|
||||
|
||||
### Send
|
||||
|
||||
The manager component (for example a radio manager) initiates the transfer of send data by calling the "send" port.
|
||||
The caller will provide a `Fw::Buffer` containing the data to send. The driver component **must** perform a callback on its `sendReturnOut` port to return the status of that send as well as returning ownership of the `Fw::Buffer` to the caller.
|
||||
The manager component (for example a radio manager) initiates the transfer of send data by calling the "send" port. The caller will provide a `Fw::Buffer` containing the data to send.
|
||||
|
||||
1. Async case: The driver component **must** perform a callback on its `sendReturnOut` port to return the status of that send as well as returning ownership of the `Fw::Buffer` to the caller.
|
||||
2. Sync case: The driver component **must** return a status of the send operation and ownership of the `Fw::Buffer` to the caller.
|
||||
|
||||
These responses are an enumeration whose values are described in the following table:
|
||||
|
||||
| Value | Description | Buffer Ownership |
|
||||
@ -19,8 +24,7 @@ These responses are an enumeration whose values are described in the following t
|
||||
|
||||
### Receive
|
||||
|
||||
In the callback formation, the byte stream driver component initiates the transfer of received data by calling the
|
||||
"recv" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
|
||||
The byte stream driver component initiates the transfer of received data by calling the "recv" output port. This port transfers any read data in a `Fw::Buffer` along with a status for the receive.
|
||||
This status is an enumeration whose values are described in the following table:
|
||||
|
||||
| Value | Description |
|
||||
@ -29,11 +33,11 @@ This status is an enumeration whose values are described in the following table:
|
||||
| ByteStreamStatus::RECV_NO_DATA | Receive worked, but there was no data |
|
||||
| ByteStreamStatus::OTHER_ERROR | Receive produced an error and buffer contains no valid data. |
|
||||
|
||||
The following components implement the byte stream model using a callback formation:
|
||||
The following components implement the byte stream model using the synchronous interface:
|
||||
- [`Drv::TcpClient`](../../TcpClient/docs/sdd.md): a F´ component wrapper of the tcp client
|
||||
- [`Drv::TcpServer`](../../TcpServer/docs/sdd.md): a F´ component wrapper of the tcp server
|
||||
- [`Drv::Udp`](../../Udp/docs/sdd.md): a F´ component wrapper of the udp
|
||||
- `Drv::LinuxUartDriver`
|
||||
- [`Drv::LinuxUartDriver`](../../LinuxUartDriver/docs/sdd.md): a F´ component wrapper of the Linux UART driver
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
@ -5,11 +5,13 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Interfaces/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/")
|
||||
|
||||
# Components
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/AsyncByteStreamBufferAdapter/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ByteStreamBufferAdapter/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ByteStreamDriverModel/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxGpioDriver/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxUartDriver/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxSpiDriver/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxI2cDriver/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxSpiDriver/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxUartDriver/")
|
||||
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ip/")
|
||||
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TcpClient/")
|
||||
|
||||
23
Drv/Interfaces/AsyncByteStreamDriver.fpp
Normal file
23
Drv/Interfaces/AsyncByteStreamDriver.fpp
Normal file
@ -0,0 +1,23 @@
|
||||
module Drv {
|
||||
# In the asynchronous ByteStreamDriver interface, the send operation is non-blocking,
|
||||
# and returns status through the sendReturnOut callback
|
||||
|
||||
@ Asynchronous ByteStreamDriver interface
|
||||
interface AsyncByteStreamDriver {
|
||||
@ Port invoked when the driver is ready to send/receive data
|
||||
output port ready: Drv.ByteStreamReady
|
||||
|
||||
@ Port invoked by the driver when it receives data
|
||||
output port $recv: Drv.ByteStreamData
|
||||
|
||||
@ Invoke this port to send data out the driver (asynchronous)
|
||||
@ Status and ownership of the buffer are returned through the sendReturnOut callback
|
||||
async input port $send: Fw.BufferSend
|
||||
|
||||
@ Port returning ownership of data received on $send port
|
||||
output port sendReturnOut: Drv.ByteStreamData
|
||||
|
||||
@ Port receiving back ownership of data sent out on $recv port
|
||||
guarded input port recvReturnIn: Fw.BufferSend
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,8 @@
|
||||
module Drv {
|
||||
# In the synchronous ByteStreamDriver interface, the send operation is blocking
|
||||
# and returns a send status
|
||||
|
||||
@ Synchronous ByteStreamDriver interface
|
||||
interface ByteStreamDriver {
|
||||
@ Port invoked when the driver is ready to send/receive data
|
||||
output port ready: Drv.ByteStreamReady
|
||||
@ -6,11 +10,9 @@ module Drv {
|
||||
@ Port invoked by the driver when it receives data
|
||||
output port $recv: Drv.ByteStreamData
|
||||
|
||||
@ Invoke this port to send data out the driver
|
||||
guarded input port $send: Fw.BufferSend
|
||||
|
||||
@ Port returning ownership of data received on $send port
|
||||
output port sendReturnOut: Drv.ByteStreamData
|
||||
@ Invoke this port to send data out the driver (synchronous)
|
||||
@ Status is returned, and ownership of the buffer is retained by the caller
|
||||
guarded input port $send: Drv.ByteStreamSend
|
||||
|
||||
@ Port receiving back ownership of data sent out on $recv port
|
||||
guarded input port recvReturnIn: Fw.BufferSend
|
||||
|
||||
@ -8,9 +8,15 @@
|
||||
register_fprime_module(
|
||||
Drv_Interfaces
|
||||
AUTOCODER_INPUTS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/AsyncByteStreamDriver.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ByteStreamDriver.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Gpio.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/I2c.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/PassiveAsyncByteStreamDriverClient.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/PassiveBufferDriver.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/PassiveBufferDriverClient.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/PassiveByteStreamDriverClient.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/PassiveByteStreamDriverClientReadyRecv.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Spi.fpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Tick.fpp"
|
||||
INTERFACE
|
||||
|
||||
25
Drv/Interfaces/PassiveAsyncByteStreamDriverClient.fpp
Normal file
25
Drv/Interfaces/PassiveAsyncByteStreamDriverClient.fpp
Normal file
@ -0,0 +1,25 @@
|
||||
module Drv {
|
||||
|
||||
@ The send interface of passive client of an asynchronous byte stream driver
|
||||
interface PassiveByteStreamDriverClientSendAsync {
|
||||
|
||||
@ Port for sending data to the driver
|
||||
@ Sample connection: client.toByteStreamDriver -> driver.$send
|
||||
output port toByteStreamDriver: Fw.BufferSend
|
||||
|
||||
@ Port for receiving buffers sent on toByteStreamDriver and then returned
|
||||
@ Sample connection: driver.sendReturnOut -> client.toByteStreamDriverReturn
|
||||
sync input port toByteStreamDriverReturn: Drv.ByteStreamData
|
||||
|
||||
}
|
||||
|
||||
@ A passive client of an asynchronous byte stream driver
|
||||
interface PassiveAsyncByteStreamDriverClient {
|
||||
|
||||
import PassiveByteStreamDriverClientReadyRecv
|
||||
|
||||
import PassiveByteStreamDriverClientSendAsync
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
14
Drv/Interfaces/PassiveBufferDriver.fpp
Normal file
14
Drv/Interfaces/PassiveBufferDriver.fpp
Normal file
@ -0,0 +1,14 @@
|
||||
module Drv {
|
||||
|
||||
@ A passive buffer driver
|
||||
interface PassiveBufferDriver {
|
||||
|
||||
@ The interface for sending data to the driver
|
||||
import Fw.PassiveBufferIn
|
||||
|
||||
@ The interface for receiving data from the driver
|
||||
import Fw.PassiveBufferOut
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
40
Drv/Interfaces/PassiveBufferDriverClient.fpp
Normal file
40
Drv/Interfaces/PassiveBufferDriverClient.fpp
Normal file
@ -0,0 +1,40 @@
|
||||
module Drv {
|
||||
|
||||
@ The send interface of passive client of a buffer driver
|
||||
interface PassiveBufferDriverClientSend {
|
||||
|
||||
@ Port for sending data to the driver
|
||||
@ Sample connection: client.toBufferDriver -> driver.bufferIn
|
||||
output port toBufferDriver: Fw.BufferSend
|
||||
|
||||
@ Port for receiving buffers sent on toBufferDriver and then returned
|
||||
@ Sample connection: driver.bufferInReturn -> client.toBufferDriverReturn
|
||||
sync input port toBufferDriverReturn: Fw.BufferSend
|
||||
|
||||
}
|
||||
|
||||
@ The receive interface of passive client of a buffer driver
|
||||
interface PassiveBufferDriverClientRecv {
|
||||
|
||||
@ Port for receiving data from the driver
|
||||
@ Sample connection: driver.bufferOut -> client.fromBufferDriver
|
||||
sync input port fromBufferDriver: Fw.BufferSend
|
||||
|
||||
@ Port for returning buffers received on fromBufferDriver
|
||||
@ Sample connection: client.fromBufferDriverReturn -> driver.bufferOutReturn
|
||||
output port fromBufferDriverReturn: Fw.BufferSend
|
||||
|
||||
}
|
||||
|
||||
@ A passive client of a buffer driver
|
||||
interface PassiveBufferDriverClient {
|
||||
|
||||
@ The interface for sending data to the driver
|
||||
import PassiveBufferDriverClientSend
|
||||
|
||||
@ The interface for receiving data from the driver
|
||||
import PassiveBufferDriverClientRecv
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
22
Drv/Interfaces/PassiveByteStreamDriverClient.fpp
Normal file
22
Drv/Interfaces/PassiveByteStreamDriverClient.fpp
Normal file
@ -0,0 +1,22 @@
|
||||
module Drv {
|
||||
|
||||
@ The send interface of passive client of a synchronous byte stream driver
|
||||
interface PassiveByteStreamDriverClientSendSync {
|
||||
|
||||
@ Port for sending data to the driver
|
||||
@ Sample connection: client.toByteStreamDriver -> driver.$send
|
||||
output port toByteStreamDriver: Drv.ByteStreamSend
|
||||
|
||||
}
|
||||
|
||||
@ A passive client of a synchronous byte stream driver
|
||||
interface PassiveByteStreamDriverClient {
|
||||
|
||||
@ The ready and receive interfaces
|
||||
import PassiveByteStreamDriverClientReadyRecv
|
||||
|
||||
@ The send interface
|
||||
import PassiveByteStreamDriverClientSendSync
|
||||
|
||||
}
|
||||
}
|
||||
26
Drv/Interfaces/PassiveByteStreamDriverClientReadyRecv.fpp
Normal file
26
Drv/Interfaces/PassiveByteStreamDriverClientReadyRecv.fpp
Normal file
@ -0,0 +1,26 @@
|
||||
module Drv {
|
||||
|
||||
@ The ready and receive interfaces for a byte stream driver client
|
||||
interface PassiveByteStreamDriverClientReadyRecv {
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Ready interface
|
||||
# ----------------------------------------------------------------------
|
||||
@ Port for receiving ready signals from the driver
|
||||
@ Sample connection: byteStreamDriver.ready -> byteStreamDriverClient.byteStreamDriverReady
|
||||
sync input port byteStreamDriverReady: Drv.ByteStreamReady
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Receive interface
|
||||
# ----------------------------------------------------------------------
|
||||
@ Port for receiving data from the driver
|
||||
@ Sample connection: byteStreamDriver.$recv -> byteStreamDriverClient.fromDriver
|
||||
sync input port fromByteStreamDriver: Drv.ByteStreamData
|
||||
|
||||
@ Port for returning ownership of buffers received on fromDriver
|
||||
@ Sample connection: byteStreamDriverClient.byteStreamReturn -> byteStreamDriver.recvReturnIn
|
||||
output port fromByteStreamDriverReturn: Fw.BufferSend
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
module Drv {
|
||||
interface Spi {
|
||||
@ Port to perform a synchronous write/read operation over the SPI bus
|
||||
guarded input port SpiWriteRead: Drv.SpiWriteRead
|
||||
|
||||
@ DEPRECATED Use SpiWriteRead port instead (same operation with a return value)
|
||||
@ Port to perform a synchronous read/write operation over the SPI bus
|
||||
sync input port SpiReadWrite: Drv.SpiReadWrite
|
||||
}
|
||||
|
||||
@ -9,12 +9,12 @@
|
||||
// acknowledged.
|
||||
//
|
||||
// ======================================================================
|
||||
#include <cstring>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Fw/Types/Assert.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Types/StringUtils.hpp>
|
||||
#include <sys/time.h>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Types/Assert.hpp>
|
||||
#include <Fw/Types/StringUtils.hpp>
|
||||
#include <cstring>
|
||||
|
||||
// This implementation has primarily implemented to isolate
|
||||
// the socket interface from the F' Fw::Buffer class.
|
||||
@ -22,42 +22,43 @@
|
||||
// the m_data member in Fw::Buffer.
|
||||
|
||||
#ifdef TGT_OS_TYPE_VXWORKS
|
||||
#include <socket.h>
|
||||
#include <inetLib.h>
|
||||
#include <errnoLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <inetLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <sockLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <socket.h>
|
||||
#include <sysLib.h>
|
||||
#include <errnoLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <cstring>
|
||||
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <arpa/inet.h>
|
||||
#else
|
||||
#error OS not supported for IP Socket Communications
|
||||
#endif
|
||||
|
||||
|
||||
namespace Drv {
|
||||
|
||||
IpSocket::IpSocket() : m_timeoutSeconds(0), m_timeoutMicroseconds(0), m_port(0) {
|
||||
::memset(m_hostname, 0, sizeof(m_hostname));
|
||||
}
|
||||
|
||||
SocketIpStatus IpSocket::configure(const char* const hostname, const U16 port, const U32 timeout_seconds, const U32 timeout_microseconds) {
|
||||
SocketIpStatus IpSocket::configure(const char* const hostname,
|
||||
const U16 port,
|
||||
const U32 timeout_seconds,
|
||||
const U32 timeout_microseconds) {
|
||||
FW_ASSERT(timeout_microseconds < 1000000, static_cast<FwAssertArgType>(timeout_microseconds));
|
||||
FW_ASSERT(this->isValidPort(port), static_cast<FwAssertArgType>(port));
|
||||
FW_ASSERT(hostname != nullptr);
|
||||
this->m_timeoutSeconds = timeout_seconds;
|
||||
this->m_timeoutMicroseconds = timeout_microseconds;
|
||||
this->m_port = port;
|
||||
(void) Fw::StringUtils::string_copy(this->m_hostname, hostname, static_cast<FwSizeType>(SOCKET_MAX_HOSTNAME_SIZE));
|
||||
(void)Fw::StringUtils::string_copy(this->m_hostname, hostname, static_cast<FwSizeType>(SOCKET_MAX_HOSTNAME_SIZE));
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
@ -75,7 +76,7 @@ SocketIpStatus IpSocket::setupTimeouts(int socketFd) {
|
||||
timeout.tv_sec = static_cast<time_t>(this->m_timeoutSeconds);
|
||||
timeout.tv_usec = static_cast<suseconds_t>(this->m_timeoutMicroseconds);
|
||||
// set socket write to timeout after 1 sec
|
||||
if (setsockopt(socketFd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&timeout), sizeof(timeout)) < 0) {
|
||||
if (setsockopt(socketFd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout)) < 0) {
|
||||
return SOCK_FAILED_TO_SET_SOCKET_OPTIONS;
|
||||
}
|
||||
#endif
|
||||
@ -128,15 +129,14 @@ SocketIpStatus IpSocket::open(SocketDescriptor& socketDescriptor) {
|
||||
return status;
|
||||
}
|
||||
|
||||
SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||
FW_ASSERT(socketDescriptor.fd != -1, static_cast<FwAssertArgType>(socketDescriptor.fd));
|
||||
SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
|
||||
FW_ASSERT(data != nullptr);
|
||||
FW_ASSERT(size > 0);
|
||||
|
||||
U32 total = 0;
|
||||
I32 sent = 0;
|
||||
|
||||
FwSizeType total = 0;
|
||||
FwSignedSizeType sent = 0;
|
||||
// Attempt to send out data and retry as necessary
|
||||
for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (total < size); i++) {
|
||||
for (FwSizeType i = 0; (i < SOCKET_MAX_ITERATIONS) && (total < size); i++) {
|
||||
errno = 0;
|
||||
// Send using my specific protocol
|
||||
sent = this->sendProtocol(socketDescriptor, data + total, size - total);
|
||||
@ -153,7 +153,7 @@ SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8
|
||||
return SOCK_SEND_ERROR;
|
||||
}
|
||||
FW_ASSERT(sent > 0, static_cast<FwAssertArgType>(sent));
|
||||
total += static_cast<U32>(sent);
|
||||
total += static_cast<FwSizeType>(sent);
|
||||
}
|
||||
// Failed to retry enough to send all data
|
||||
if (total < size) {
|
||||
@ -164,16 +164,16 @@ SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data, U32& req_read) {
|
||||
//TODO: Uncomment FW_ASSERT for socketDescriptor.fd once we fix TcpClientTester to not pass in uninitialized socketDescriptor
|
||||
// FW_ASSERT(socketDescriptor.fd != -1, static_cast<FwAssertArgType>(socketDescriptor.fd));
|
||||
SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data, FwSizeType& req_read) {
|
||||
// TODO: Uncomment FW_ASSERT for socketDescriptor.fd once we fix TcpClientTester to not pass in uninitialized
|
||||
// socketDescriptor
|
||||
// FW_ASSERT(socketDescriptor.fd != -1, static_cast<FwAssertArgType>(socketDescriptor.fd));
|
||||
FW_ASSERT(data != nullptr);
|
||||
|
||||
|
||||
I32 bytes_received_or_status; // Stores the return value from recvProtocol
|
||||
FwSignedSizeType bytes_received_or_status; // Stores the return value from recvProtocol
|
||||
|
||||
// Loop primarily for EINTR. Other conditions should lead to an earlier exit.
|
||||
for (U32 i = 0; i < SOCKET_MAX_ITERATIONS; i++) {
|
||||
for (FwSizeType i = 0; i < SOCKET_MAX_ITERATIONS; i++) {
|
||||
errno = 0;
|
||||
// Pass the current value of req_read (max buffer size) to recvProtocol.
|
||||
// recvProtocol returns bytes read or -1 on error.
|
||||
@ -181,13 +181,13 @@ SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data
|
||||
|
||||
if (bytes_received_or_status > 0) {
|
||||
// Successfully read data
|
||||
req_read = static_cast<U32>(bytes_received_or_status);
|
||||
req_read = static_cast<FwSizeType>(bytes_received_or_status);
|
||||
return SOCK_SUCCESS;
|
||||
} else if (bytes_received_or_status == 0) {
|
||||
// Handle zero return based on protocol-specific behavior
|
||||
req_read = 0;
|
||||
return this->handleZeroReturn();
|
||||
} else { // bytes_received_or_status == -1, an error occurred
|
||||
} else { // bytes_received_or_status == -1, an error occurred
|
||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
// Non-blocking socket would block, or SO_RCVTIMEO timeout occurred.
|
||||
req_read = 0;
|
||||
@ -195,7 +195,7 @@ SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data
|
||||
} else if ((errno == ECONNRESET) || (errno == EBADF)) {
|
||||
// Connection reset or bad file descriptor.
|
||||
req_read = 0;
|
||||
return SOCK_DISCONNECTED; // Or a more specific error like SOCK_READ_ERROR
|
||||
return SOCK_DISCONNECTED; // Or a more specific error like SOCK_READ_ERROR
|
||||
} else {
|
||||
// Other socket read error.
|
||||
req_read = 0;
|
||||
@ -214,4 +214,22 @@ SocketIpStatus IpSocket::handleZeroReturn() {
|
||||
return SOCK_DISCONNECTED;
|
||||
}
|
||||
|
||||
SocketIpStatus IpSocket::setupSocketOptions(int socketFd) {
|
||||
// Iterate over the socket options and set them
|
||||
for (const auto& options : IP_SOCKET_OPTIONS) {
|
||||
int status = 0;
|
||||
if (options.type == SOCK_OPT_INT) {
|
||||
status = setsockopt(socketFd, options.level, options.option, &options.value.intVal,
|
||||
sizeof(options.value.intVal));
|
||||
} else {
|
||||
status = setsockopt(socketFd, options.level, options.option, &options.value.sizeVal,
|
||||
sizeof(options.value.sizeVal));
|
||||
}
|
||||
if (status) {
|
||||
return SOCK_FAILED_TO_SET_SOCKET_OPTIONS;
|
||||
}
|
||||
}
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
@ -13,14 +13,14 @@
|
||||
#define DRV_IP_IPHELPER_HPP_
|
||||
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <config/IpCfg.hpp>
|
||||
#include <Os/Mutex.hpp>
|
||||
#include <config/IpCfg.hpp>
|
||||
|
||||
namespace Drv {
|
||||
|
||||
struct SocketDescriptor final {
|
||||
int fd = -1; //!< Used for all sockets to track the communication file descriptor
|
||||
int serverFd = -1; //!< Used for server sockets to track the listening file descriptor
|
||||
int fd = -1; //!< Used for all sockets to track the communication file descriptor
|
||||
int serverFd = -1; //!< Used for server sockets to track the listening file descriptor
|
||||
};
|
||||
|
||||
/**
|
||||
@ -57,7 +57,7 @@ enum SocketIpStatus {
|
||||
class IpSocket {
|
||||
public:
|
||||
IpSocket();
|
||||
virtual ~IpSocket(){};
|
||||
virtual ~IpSocket() {};
|
||||
/**
|
||||
* \brief configure the ip socket with host and transmission timeouts
|
||||
*
|
||||
@ -76,7 +76,9 @@ class IpSocket {
|
||||
* \param send_timeout_microseconds: send timeout microseconds portion. Must be less than 1000000
|
||||
* \return status of configure
|
||||
*/
|
||||
virtual SocketIpStatus configure(const char* hostname, const U16 port, const U32 send_timeout_seconds,
|
||||
virtual SocketIpStatus configure(const char* hostname,
|
||||
const U16 port,
|
||||
const U32 send_timeout_seconds,
|
||||
const U32 send_timeout_microseconds);
|
||||
|
||||
/**
|
||||
@ -114,7 +116,7 @@ class IpSocket {
|
||||
* \param size: size of data to send
|
||||
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
||||
*/
|
||||
virtual SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size);
|
||||
virtual SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size);
|
||||
/**
|
||||
* \brief receive data from the IP socket from the given buffer
|
||||
*
|
||||
@ -131,7 +133,7 @@ class IpSocket {
|
||||
* \param size: maximum size of data buffer to fill
|
||||
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
||||
*/
|
||||
SocketIpStatus recv(const SocketDescriptor& fd, U8* const data, U32& size);
|
||||
SocketIpStatus recv(const SocketDescriptor& fd, U8* const data, FwSizeType& size);
|
||||
|
||||
/**
|
||||
* \brief closes the socket
|
||||
@ -173,7 +175,7 @@ class IpSocket {
|
||||
* \brief setup the socket timeout properties of the opened outgoing socket
|
||||
* \param socketDescriptor: socket descriptor to setup
|
||||
* \return status of timeout setup
|
||||
*/
|
||||
*/
|
||||
SocketIpStatus setupTimeouts(int socketFd);
|
||||
|
||||
/**
|
||||
@ -196,7 +198,9 @@ class IpSocket {
|
||||
* \param size: size of data to send
|
||||
* \return: size of data sent, or -1 on error.
|
||||
*/
|
||||
virtual I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) = 0;
|
||||
virtual FwSignedSizeType sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) = 0;
|
||||
|
||||
/**
|
||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||
@ -205,7 +209,9 @@ class IpSocket {
|
||||
* \param size: size of data buffer
|
||||
* \return: size of data received, or -1 on error.
|
||||
*/
|
||||
virtual I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) = 0;
|
||||
virtual FwSignedSizeType recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) = 0;
|
||||
|
||||
/**
|
||||
* \brief Handle zero return from recvProtocol
|
||||
@ -218,9 +224,16 @@ class IpSocket {
|
||||
*/
|
||||
virtual SocketIpStatus handleZeroReturn();
|
||||
|
||||
/**
|
||||
* \brief setup the socket options of the input socket as defined in IpCfg.hpp
|
||||
* \param socketFd: socket file descriptor
|
||||
* \return status of setup options
|
||||
*/
|
||||
SocketIpStatus setupSocketOptions(int socketFd);
|
||||
|
||||
U32 m_timeoutSeconds;
|
||||
U32 m_timeoutMicroseconds;
|
||||
U16 m_port; //!< IP address port used
|
||||
U16 m_port; //!< IP address port used
|
||||
char m_hostname[SOCKET_MAX_HOSTNAME_SIZE]; //!< Hostname to supply
|
||||
};
|
||||
} // namespace Drv
|
||||
|
||||
@ -21,11 +21,27 @@ SocketComponentHelper::SocketComponentHelper() {}
|
||||
|
||||
SocketComponentHelper::~SocketComponentHelper() {}
|
||||
|
||||
void SocketComponentHelper::start(const Fw::StringBase &name,
|
||||
void SocketComponentHelper::start(const Fw::ConstStringBase& name,
|
||||
const FwTaskPriorityType priority,
|
||||
const Os::Task::ParamType stack,
|
||||
const Os::Task::ParamType cpuAffinity) {
|
||||
FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
||||
const Os::Task::ParamType cpuAffinity,
|
||||
const FwTaskPriorityType priorityReconnect,
|
||||
const Os::Task::ParamType stackReconnect,
|
||||
const Os::Task::ParamType cpuAffinityReconnect) {
|
||||
// Reconnect Thread
|
||||
FW_ASSERT(m_reconnectTask.getState() ==
|
||||
Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
||||
this->m_reconnectStop = false;
|
||||
Fw::String reconnectName;
|
||||
reconnectName.format("%s_reconnect", name.toChar());
|
||||
Os::Task::Arguments reconnectArguments(reconnectName, SocketComponentHelper::reconnectTask, this, priorityReconnect,
|
||||
stackReconnect, cpuAffinityReconnect);
|
||||
Os::Task::Status reconnectStat = m_reconnectTask.start(reconnectArguments);
|
||||
FW_ASSERT(Os::Task::OP_OK == reconnectStat, static_cast<FwAssertArgType>(reconnectStat));
|
||||
|
||||
// Read Thread
|
||||
FW_ASSERT(m_task.getState() ==
|
||||
Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
||||
this->m_stop = false;
|
||||
// Note: the first step is for the IP socket to open the port
|
||||
Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
|
||||
@ -45,7 +61,6 @@ SocketIpStatus SocketComponentHelper::open() {
|
||||
} else {
|
||||
local_open = OpenState::SKIP;
|
||||
}
|
||||
|
||||
}
|
||||
if (local_open == OpenState::OPENING) {
|
||||
FW_ASSERT(this->m_descriptor.fd == -1); // Ensure we are not opening an opened socket
|
||||
@ -80,18 +95,19 @@ void SocketComponentHelper::setAutomaticOpen(bool auto_open) {
|
||||
this->m_reopen = auto_open;
|
||||
}
|
||||
|
||||
bool SocketComponentHelper::getAutomaticOpen() {
|
||||
Os::ScopeLock scopedLock(this->m_lock);
|
||||
return this->m_reopen;
|
||||
}
|
||||
|
||||
SocketIpStatus SocketComponentHelper::reopen() {
|
||||
SocketIpStatus status = SOCK_SUCCESS;
|
||||
if (not this->isOpened()) {
|
||||
// Check for auto-open before attempting to reopen
|
||||
bool reopen = false;
|
||||
{
|
||||
Os::ScopeLock scopedLock(this->m_lock);
|
||||
reopen = this->m_reopen;
|
||||
}
|
||||
// Open a network connection if it has not already been open
|
||||
bool reopen = this->getAutomaticOpen();
|
||||
if (not reopen) {
|
||||
status = SOCK_AUTO_CONNECT_DISABLED;
|
||||
// Open a network connection if it has not already been open
|
||||
} else {
|
||||
status = this->open();
|
||||
if (status == SocketIpStatus::SOCK_ANOTHER_THREAD_OPENING) {
|
||||
@ -102,22 +118,23 @@ SocketIpStatus SocketComponentHelper::reopen() {
|
||||
return status;
|
||||
}
|
||||
|
||||
SocketIpStatus SocketComponentHelper::send(const U8* const data, const U32 size) {
|
||||
SocketIpStatus SocketComponentHelper::send(const U8* const data, const FwSizeType size) {
|
||||
SocketIpStatus status = SOCK_SUCCESS;
|
||||
this->m_lock.lock();
|
||||
SocketDescriptor descriptor = this->m_descriptor;
|
||||
this->m_lock.unlock();
|
||||
// Prevent transmission before connection, or after a disconnect
|
||||
if (descriptor.fd == -1) {
|
||||
status = this->reopen();
|
||||
// if reopen wasn't successful, pass the that up to the caller
|
||||
if(status != SOCK_SUCCESS) {
|
||||
return status;
|
||||
this->requestReconnect();
|
||||
SocketIpStatus reconnectStat = this->waitForReconnect();
|
||||
if (reconnectStat == SOCK_SUCCESS) {
|
||||
// Refresh local copy after reopen
|
||||
this->m_lock.lock();
|
||||
descriptor = this->m_descriptor;
|
||||
this->m_lock.unlock();
|
||||
} else {
|
||||
return reconnectStat;
|
||||
}
|
||||
// Refresh local copy after reopen
|
||||
this->m_lock.lock();
|
||||
descriptor = this->m_descriptor;
|
||||
this->m_lock.unlock();
|
||||
}
|
||||
status = this->getSocketHandler().send(descriptor, data, size);
|
||||
if (status == SOCK_DISCONNECTED) {
|
||||
@ -138,8 +155,15 @@ void SocketComponentHelper::close() {
|
||||
this->m_open = OpenState::NOT_OPEN;
|
||||
}
|
||||
|
||||
/* Read Thread */
|
||||
|
||||
Os::Task::Status SocketComponentHelper::join() {
|
||||
return m_task.join();
|
||||
Os::Task::Status stat = m_task.join();
|
||||
Os::Task::Status reconnectStat = this->joinReconnect();
|
||||
if (stat == Os::Task::Status::OP_OK) {
|
||||
return reconnectStat;
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
void SocketComponentHelper::stop() {
|
||||
@ -148,6 +172,7 @@ void SocketComponentHelper::stop() {
|
||||
Os::ScopeLock scopeLock(m_lock);
|
||||
this->m_stop = true;
|
||||
}
|
||||
this->stopReconnect();
|
||||
this->shutdown(); // Break out of any receives and fully shutdown
|
||||
}
|
||||
|
||||
@ -157,7 +182,7 @@ bool SocketComponentHelper::running() {
|
||||
return running;
|
||||
}
|
||||
|
||||
SocketIpStatus SocketComponentHelper::recv(U8* data, U32 &size) {
|
||||
SocketIpStatus SocketComponentHelper::recv(U8* data, FwSizeType& size) {
|
||||
SocketIpStatus status = SOCK_SUCCESS;
|
||||
// Check for previously disconnected socket
|
||||
this->m_lock.lock();
|
||||
@ -178,31 +203,25 @@ void SocketComponentHelper::readLoop() {
|
||||
do {
|
||||
// Prevent transmission before connection, or after a disconnect
|
||||
if ((not this->isOpened()) and this->running()) {
|
||||
status = this->reopen();
|
||||
this->requestReconnect();
|
||||
status = this->waitForReconnect();
|
||||
// When reopen is disabled, just break as this is a exit condition for the loop
|
||||
if (status == SOCK_AUTO_CONNECT_DISABLED) {
|
||||
break;
|
||||
}
|
||||
// If the reconnection failed in any other way, warn, wait, and retry
|
||||
else if (status != SOCK_SUCCESS) {
|
||||
Fw::Logger::log("[WARNING] Failed to open port with status %d and errno %d\n", status, errno);
|
||||
(void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// If the network connection is open, read from it
|
||||
if (this->isOpened() and this->running()) {
|
||||
Fw::Buffer buffer = this->getBuffer();
|
||||
U8* data = buffer.getData();
|
||||
FW_ASSERT(data);
|
||||
FW_ASSERT_NO_OVERFLOW(buffer.getSize(), U32);
|
||||
U32 size = static_cast<U32>(buffer.getSize());
|
||||
FwSizeType size = buffer.getSize();
|
||||
// recv blocks, so it may have been a while since its done an isOpened check
|
||||
status = this->recv(data, size);
|
||||
if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) && (status != SOCK_NO_DATA_AVAILABLE)) {
|
||||
Fw::Logger::log("[WARNING] Failed to recv from port with status %d and errno %d\n",
|
||||
status,
|
||||
errno);
|
||||
if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) &&
|
||||
(status != SOCK_NO_DATA_AVAILABLE)) {
|
||||
Fw::Logger::log("[WARNING] %s failed to recv from port with status %d and errno %d\n",
|
||||
this->m_task.getName().toChar(), status, errno);
|
||||
this->close();
|
||||
buffer.setSize(0);
|
||||
} else {
|
||||
@ -215,7 +234,7 @@ void SocketComponentHelper::readLoop() {
|
||||
// This will loop until stopped. If auto-open is disabled, this will break when reopen returns disabled status
|
||||
while (this->running());
|
||||
// Close the socket
|
||||
this->close(); // Close the port entirely
|
||||
this->close(); // Close the port entirely
|
||||
}
|
||||
|
||||
void SocketComponentHelper::readTask(void* pointer) {
|
||||
@ -223,4 +242,124 @@ void SocketComponentHelper::readTask(void* pointer) {
|
||||
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
|
||||
self->readLoop();
|
||||
}
|
||||
|
||||
/* Reconnect Thread */
|
||||
|
||||
Os::Task::Status SocketComponentHelper::joinReconnect() {
|
||||
return m_reconnectTask.join();
|
||||
}
|
||||
|
||||
void SocketComponentHelper::stopReconnect() {
|
||||
Os::ScopeLock scopeLock(this->m_reconnectLock);
|
||||
this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
||||
this->m_reconnectStop = true;
|
||||
}
|
||||
|
||||
bool SocketComponentHelper::runningReconnect() {
|
||||
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
||||
bool running = not this->m_reconnectStop;
|
||||
return running;
|
||||
}
|
||||
|
||||
void SocketComponentHelper::reconnectLoop() {
|
||||
SocketIpStatus status = SOCK_SUCCESS;
|
||||
while (this->runningReconnect()) {
|
||||
// Check if we need to reconnect
|
||||
bool reconnect = false;
|
||||
{
|
||||
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
||||
if (this->m_reconnectState == ReconnectState::REQUEST_RECONNECT) {
|
||||
this->m_reconnectState = ReconnectState::RECONNECT_IN_PROGRESS;
|
||||
reconnect = true;
|
||||
|
||||
}
|
||||
// If we were already in or are now in RECONNECT_IN_PROGRESS we
|
||||
// need to try to reconnect, again
|
||||
else if (this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS) {
|
||||
reconnect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reconnect) {
|
||||
status = this->reopen();
|
||||
|
||||
// Reopen Case 1: Auto Connect is disabled, so no longer
|
||||
// try to reconnect
|
||||
if (status == SOCK_AUTO_CONNECT_DISABLED) {
|
||||
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
||||
this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
||||
}
|
||||
// Reopen Case 2: Success, so no longer
|
||||
// try to reconnect
|
||||
else if (status == SOCK_SUCCESS) {
|
||||
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
||||
this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
||||
}
|
||||
// Reopen Case 3: Keep trying to reconnect - NO reconnect
|
||||
// state change
|
||||
else {
|
||||
Fw::Logger::log("[WARNING] %s failed to open port with status %d and errno %d\n",
|
||||
this->m_task.getName().toChar(), status, errno);
|
||||
(void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
|
||||
}
|
||||
} else {
|
||||
// After a brief delay, we will loop again
|
||||
(void)Os::Task::delay(this->m_reconnectCheckInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SocketComponentHelper::reconnectTask(void* pointer) {
|
||||
FW_ASSERT(pointer);
|
||||
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
|
||||
self->reconnectLoop();
|
||||
}
|
||||
|
||||
void SocketComponentHelper::requestReconnect() {
|
||||
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
||||
if (m_reconnectState == ReconnectState::NOT_RECONNECTING) {
|
||||
m_reconnectState = ReconnectState::REQUEST_RECONNECT;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SocketIpStatus SocketComponentHelper::waitForReconnect(Fw::TimeInterval timeout) {
|
||||
// Do not attempt to reconnect if auto reconnect config flag is disabled
|
||||
if (!this->getAutomaticOpen()) {
|
||||
return SOCK_AUTO_CONNECT_DISABLED;
|
||||
}
|
||||
|
||||
Fw::TimeInterval elapsed = Fw::TimeInterval(0, 0);
|
||||
|
||||
while (elapsed < timeout) {
|
||||
// If the reconnect thread is NOT reconnecting, we are done waiting
|
||||
// If we are no longer running the reconnect thread, we are done waiting
|
||||
{
|
||||
Os::ScopeLock scopedLock(this->m_reconnectLock);
|
||||
if (this->m_reconnectState == ReconnectState::NOT_RECONNECTING) {
|
||||
break;
|
||||
}
|
||||
if (this->m_reconnectStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Wait a bit before checking again
|
||||
(void)Os::Task::delay(this->m_reconnectWaitInterval);
|
||||
elapsed.add(this->m_reconnectWaitInterval.getSeconds(), this->m_reconnectWaitInterval.getUSeconds());
|
||||
}
|
||||
|
||||
// If we have completed our loop, check if we are connected or if
|
||||
// auto connect was disabled during our wait
|
||||
if (this->isOpened()) {
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
// Check one more time if auto reconnect config flag got disabled
|
||||
if (!this->getAutomaticOpen()) {
|
||||
return SOCK_AUTO_CONNECT_DISABLED;
|
||||
}
|
||||
|
||||
return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
@ -12,10 +12,11 @@
|
||||
#ifndef DRV_SocketComponentHelper_HPP
|
||||
#define DRV_SocketComponentHelper_HPP
|
||||
|
||||
#include <Fw/Buffer/Buffer.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Os/Task.hpp>
|
||||
#include <Fw/Buffer/Buffer.hpp>
|
||||
#include <Os/Condition.hpp>
|
||||
#include <Os/Mutex.hpp>
|
||||
#include <Os/Task.hpp>
|
||||
|
||||
namespace Drv {
|
||||
/**
|
||||
@ -27,12 +28,8 @@ namespace Drv {
|
||||
*/
|
||||
class SocketComponentHelper {
|
||||
public:
|
||||
enum OpenState{
|
||||
NOT_OPEN,
|
||||
OPENING,
|
||||
OPEN,
|
||||
SKIP
|
||||
};
|
||||
enum OpenState { NOT_OPEN, OPENING, OPEN, SKIP };
|
||||
enum ReconnectState { NOT_RECONNECTING, REQUEST_RECONNECT, RECONNECT_IN_PROGRESS };
|
||||
/**
|
||||
* \brief constructs the socket read task
|
||||
*/
|
||||
@ -52,14 +49,25 @@ class SocketComponentHelper {
|
||||
* default behavior is to automatically open connections.
|
||||
*
|
||||
* \param name: name of the task
|
||||
* \param priority: priority of the started task. See: Os::Task::start. Default: TASK_PRIORITY_DEFAULT, not prioritized
|
||||
* \param stack: stack size provided to the task. See: Os::Task::start. Default: TASK_DEFAULT, posix threads default
|
||||
* \param cpuAffinity: cpu affinity provided to task. See: Os::Task::start. Default: TASK_DEFAULT, don't care
|
||||
* \param priority: priority of the started read task. See: Os::Task::start. Default: TASK_PRIORITY_DEFAULT, not
|
||||
* prioritized
|
||||
* \param stack: stack size provided to the read task. See: Os::Task::start. Default: TASK_DEFAULT, posix threads
|
||||
* default
|
||||
* \param cpuAffinity: cpu affinity provided to read task. See: Os::Task::start. Default: TASK_DEFAULT, don't care
|
||||
* \param priorityReconnect: priority of the started reconnect task. See: Os::Task::start. Default:
|
||||
* TASK_PRIORITY_DEFAULT, not prioritized
|
||||
* \param stackReconnect: stack size provided to the reconnect task. See: Os::Task::start. Default: TASK_DEFAULT,
|
||||
* posix threads default
|
||||
* \param cpuAffinityReconnect: cpu affinity provided to reconnect task. See: Os::Task::start. Default:
|
||||
* TASK_DEFAULT, don't care
|
||||
*/
|
||||
void start(const Fw::StringBase &name,
|
||||
void start(const Fw::ConstStringBase& name,
|
||||
const FwTaskPriorityType priority = Os::Task::TASK_PRIORITY_DEFAULT,
|
||||
const Os::Task::ParamType stack = Os::Task::TASK_DEFAULT,
|
||||
const Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT);
|
||||
const Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT,
|
||||
const FwTaskPriorityType priorityReconnect = Os::Task::TASK_PRIORITY_DEFAULT,
|
||||
const Os::Task::ParamType stackReconnect = Os::Task::TASK_DEFAULT,
|
||||
const Os::Task::ParamType cpuAffinityReconnect = Os::Task::TASK_DEFAULT);
|
||||
|
||||
/**
|
||||
* \brief open the socket for communications
|
||||
@ -73,7 +81,7 @@ class SocketComponentHelper {
|
||||
*/
|
||||
SocketIpStatus open();
|
||||
|
||||
/**
|
||||
/**
|
||||
* \brief check if IP socket has previously been opened
|
||||
*
|
||||
* Check if this IpSocket has previously been opened. In the case of Udp this will check for outgoing transmissions
|
||||
@ -87,13 +95,21 @@ class SocketComponentHelper {
|
||||
/**
|
||||
* \brief set socket to automatically open connections when true, or not when false
|
||||
*
|
||||
* When passed `true`, this instructs the socket to automatically open a socket and reopen socket failed connections. When passed `false`
|
||||
* the user must explicitly call the `open` method to open the socket initially and when a socket fails.
|
||||
* When passed `true`, this instructs the socket to automatically open a socket and reopen socket failed
|
||||
* connections. When passed `false` the user must explicitly call the `open` method to open the socket initially and
|
||||
* when a socket fails.
|
||||
*
|
||||
* \param auto_open: true to automatically open and reopen sockets, false otherwise
|
||||
*/
|
||||
void setAutomaticOpen(bool auto_open);
|
||||
|
||||
/**
|
||||
* \brief get socket automatically open connections status
|
||||
*
|
||||
* \return status of auto_open
|
||||
*/
|
||||
bool getAutomaticOpen();
|
||||
|
||||
/**
|
||||
* \brief send data to the IP socket from the given buffer
|
||||
*
|
||||
@ -102,7 +118,7 @@ class SocketComponentHelper {
|
||||
* \param size: size of data to send
|
||||
* \return status of send, SOCK_SUCCESS for success, something else on error
|
||||
*/
|
||||
SocketIpStatus send(const U8* const data, const U32 size);
|
||||
SocketIpStatus send(const U8* const data, const FwSizeType size);
|
||||
|
||||
/**
|
||||
* \brief receive data from the IP socket from the given buffer
|
||||
@ -112,7 +128,7 @@ class SocketComponentHelper {
|
||||
* \param size: maximum size of data buffer to fill
|
||||
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
||||
*/
|
||||
SocketIpStatus recv(U8* data, U32 &size);
|
||||
SocketIpStatus recv(U8* data, FwSizeType& size);
|
||||
|
||||
/**
|
||||
* \brief close the socket communications
|
||||
@ -137,6 +153,7 @@ class SocketComponentHelper {
|
||||
* \brief is the read loop running
|
||||
*/
|
||||
bool running();
|
||||
bool runningReconnect();
|
||||
|
||||
/**
|
||||
* \brief stop the socket read task and close the associated socket.
|
||||
@ -146,6 +163,8 @@ class SocketComponentHelper {
|
||||
*/
|
||||
void stop();
|
||||
|
||||
void stopReconnect();
|
||||
|
||||
/**
|
||||
* \brief joins to the stopping read task to wait for it to close
|
||||
*
|
||||
@ -156,11 +175,19 @@ class SocketComponentHelper {
|
||||
*/
|
||||
Os::Task::Status join();
|
||||
|
||||
Os::Task::Status joinReconnect();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief receive off the TCP socket
|
||||
*/
|
||||
virtual void readLoop();
|
||||
|
||||
/**
|
||||
* \brief reconnect TCP socket
|
||||
*/
|
||||
virtual void reconnectLoop();
|
||||
|
||||
/**
|
||||
* \brief returns a reference to the socket handler
|
||||
*
|
||||
@ -202,7 +229,6 @@ class SocketComponentHelper {
|
||||
*/
|
||||
virtual void connected() = 0;
|
||||
|
||||
|
||||
/**
|
||||
* \brief a task designed to read from the socket and output incoming data
|
||||
*
|
||||
@ -210,25 +236,64 @@ class SocketComponentHelper {
|
||||
*/
|
||||
static void readTask(void* pointer);
|
||||
|
||||
/**
|
||||
* \brief a task designed for socket reconnection
|
||||
*
|
||||
* \param pointer: pointer to "this" component
|
||||
*/
|
||||
static void reconnectTask(void* pointer);
|
||||
|
||||
/**
|
||||
* \brief signal to reconnect task that a reconnect is needed
|
||||
*
|
||||
*/
|
||||
void requestReconnect();
|
||||
|
||||
/**
|
||||
* \brief wait method for a task to wait for a reconnect request to complete
|
||||
*
|
||||
* After requesting a reconnect, tasks should call this method
|
||||
* to wait for the reconnect thread to complete
|
||||
*
|
||||
*
|
||||
* \param timeout: timeout so that the wait doesn't hang indefinitely
|
||||
*
|
||||
* \return status of the reconnect request, SOCK_DISCONNECTED for
|
||||
* reopen again, or SOCK_SUCCESS on success, something else on error
|
||||
*/
|
||||
SocketIpStatus waitForReconnect(Fw::TimeInterval timeout = Fw::TimeInterval(1, 0));
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Re-open port if it has been disconnected
|
||||
*
|
||||
* This function is a helper to handle the situations where this code needs to safely reopen a socket. User code should
|
||||
* connect using the `open` call. This is for opening/reopening in situations where automatic open is performed
|
||||
* within this socket helper.
|
||||
* This function is a helper to handle the situations where this code needs to safely reopen a socket. User code
|
||||
* should connect using the `open` call. This is for opening/reopening in situations where automatic open is
|
||||
* performed within this socket helper.
|
||||
*
|
||||
* \return status of reconnect, SOCK_SUCCESS for success, something else on error
|
||||
*/
|
||||
SocketIpStatus reopen();
|
||||
|
||||
protected:
|
||||
bool m_reopen = true; //!< Force reopen on disconnect
|
||||
SocketDescriptor m_descriptor;
|
||||
|
||||
// Read/recv
|
||||
Os::Task m_task;
|
||||
Os::Mutex m_lock;
|
||||
SocketDescriptor m_descriptor;
|
||||
bool m_reopen = true; //!< Force reopen on disconnect
|
||||
bool m_stop = true; //!< Stops the task when set to true
|
||||
OpenState m_open = OpenState::NOT_OPEN; //!< Have we successfully opened
|
||||
bool m_stop = true; //!< Stops the task when set to true
|
||||
OpenState m_open = OpenState::NOT_OPEN; //!< Have we successfully opened
|
||||
|
||||
// Reconnect
|
||||
Os::Task m_reconnectTask;
|
||||
Os::Mutex m_reconnectLock;
|
||||
bool m_reconnectStop = true;
|
||||
ReconnectState m_reconnectState = ReconnectState::NOT_RECONNECTING;
|
||||
Fw::TimeInterval m_reconnectCheckInterval =
|
||||
Fw::TimeInterval(0, 50000); // 50 ms, Interval at which reconnect task loop checks for requests
|
||||
Fw::TimeInterval m_reconnectWaitInterval =
|
||||
Fw::TimeInterval(0, 10000); // 10 ms, Interval at which reconnect requesters wait for response
|
||||
};
|
||||
}
|
||||
} // namespace Drv
|
||||
#endif // DRV_SocketComponentHelper_HPP
|
||||
|
||||
@ -11,28 +11,28 @@
|
||||
// ======================================================================
|
||||
|
||||
#include <Drv/Ip/TcpClientSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Fw/Types/Assert.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
|
||||
#ifdef TGT_OS_TYPE_VXWORKS
|
||||
#include <socket.h>
|
||||
#include <inetLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <sockLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <sysLib.h>
|
||||
#include <errnoLib.h>
|
||||
#include <cstring>
|
||||
#include <errnoLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <inetLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <sockLib.h>
|
||||
#include <socket.h>
|
||||
#include <sysLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <cstring>
|
||||
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#error OS not supported for IP Socket Communications
|
||||
#error OS not supported for IP Socket Communications
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
@ -46,7 +46,6 @@ bool TcpClientSocket::isValidPort(U16 port) {
|
||||
return port != 0;
|
||||
}
|
||||
|
||||
|
||||
SocketIpStatus TcpClientSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||
int socketFd = -1;
|
||||
struct sockaddr_in address;
|
||||
@ -70,6 +69,11 @@ SocketIpStatus TcpClientSocket::openProtocol(SocketDescriptor& socketDescriptor)
|
||||
return SOCK_INVALID_IP_ADDRESS;
|
||||
};
|
||||
|
||||
if (IpSocket::setupSocketOptions(socketFd) != SOCK_SUCCESS) {
|
||||
::close(socketFd);
|
||||
return SOCK_FAILED_TO_SET_SOCKET_OPTIONS;
|
||||
}
|
||||
|
||||
// Now apply timeouts
|
||||
if (IpSocket::setupTimeouts(socketFd) != SOCK_SUCCESS) {
|
||||
::close(socketFd);
|
||||
@ -86,12 +90,18 @@ SocketIpStatus TcpClientSocket::openProtocol(SocketDescriptor& socketDescriptor)
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
I32 TcpClientSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||
return static_cast<I32>(::send(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS));
|
||||
FwSignedSizeType TcpClientSocket::sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) {
|
||||
return static_cast<FwSignedSizeType>(
|
||||
::send(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS));
|
||||
}
|
||||
|
||||
I32 TcpClientSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
|
||||
return static_cast<I32>(::recv(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS));
|
||||
FwSignedSizeType TcpClientSocket::recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) {
|
||||
return static_cast<FwSignedSizeType>(
|
||||
::recv(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS));
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
#ifndef DRV_TCPCLIENT_TCPHELPER_HPP_
|
||||
#define DRV_TCPCLIENT_TCPHELPER_HPP_
|
||||
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <config/IpCfg.hpp>
|
||||
|
||||
namespace Drv {
|
||||
@ -29,6 +29,7 @@ class TcpClientSocket : public IpSocket {
|
||||
* \brief Constructor for client socket tcp implementation
|
||||
*/
|
||||
TcpClientSocket();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Check if the given port is valid for the socket
|
||||
@ -55,7 +56,9 @@ class TcpClientSocket : public IpSocket {
|
||||
* \param size: size of data to send
|
||||
* \return: size of data sent, or -1 on error.
|
||||
*/
|
||||
I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||
FwSignedSizeType sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) override;
|
||||
/**
|
||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||
* \param socketDescriptor: descriptor to recv from
|
||||
@ -63,7 +66,9 @@ class TcpClientSocket : public IpSocket {
|
||||
* \param size: size of data buffer
|
||||
* \return: size of data received, or -1 on error.
|
||||
*/
|
||||
I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) override;
|
||||
FwSignedSizeType recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) override;
|
||||
};
|
||||
} // namespace Drv
|
||||
|
||||
|
||||
@ -10,28 +10,28 @@
|
||||
//
|
||||
// ======================================================================
|
||||
#include <Drv/Ip/TcpServerSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Fw/Types/Assert.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
|
||||
#ifdef TGT_OS_TYPE_VXWORKS
|
||||
#include <socket.h>
|
||||
#include <inetLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <sockLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <sysLib.h>
|
||||
#include <errnoLib.h>
|
||||
#include <cstring>
|
||||
#include <errnoLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <inetLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <sockLib.h>
|
||||
#include <socket.h>
|
||||
#include <sysLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <cstring>
|
||||
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#error OS not supported for IP Socket Communications
|
||||
#error OS not supported for IP Socket Communications
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
@ -66,6 +66,11 @@ SocketIpStatus TcpServerSocket::startup(SocketDescriptor& socketDescriptor) {
|
||||
return SOCK_INVALID_IP_ADDRESS;
|
||||
};
|
||||
|
||||
if (IpSocket::setupSocketOptions(serverFd) != SOCK_SUCCESS) {
|
||||
::close(serverFd);
|
||||
return SOCK_FAILED_TO_SET_SOCKET_OPTIONS;
|
||||
}
|
||||
|
||||
// TCP requires bind to an address to the socket
|
||||
if (::bind(serverFd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
|
||||
::close(serverFd);
|
||||
@ -73,14 +78,15 @@ SocketIpStatus TcpServerSocket::startup(SocketDescriptor& socketDescriptor) {
|
||||
}
|
||||
|
||||
socklen_t size = sizeof(address);
|
||||
if (::getsockname(serverFd, reinterpret_cast<struct sockaddr *>(&address), &size) == -1) {
|
||||
if (::getsockname(serverFd, reinterpret_cast<struct sockaddr*>(&address), &size) == -1) {
|
||||
::close(serverFd);
|
||||
return SOCK_FAILED_TO_READ_BACK_PORT;
|
||||
}
|
||||
// TCP requires listening on the socket. Since we only expect a single client, set the TCP backlog (second argument) to 1 to prevent queuing of multiple clients.
|
||||
// TCP requires listening on the socket. Since we only expect a single client, set the TCP backlog (second argument)
|
||||
// to 1 to prevent queuing of multiple clients.
|
||||
if (::listen(serverFd, 1) < 0) {
|
||||
::close(serverFd);
|
||||
return SOCK_FAILED_TO_LISTEN; // What we have here is a failure to communicate
|
||||
return SOCK_FAILED_TO_LISTEN; // What we have here is a failure to communicate
|
||||
}
|
||||
Fw::Logger::log("Listening for single client at %s:%hu\n", m_hostname, m_port);
|
||||
FW_ASSERT(serverFd != -1);
|
||||
@ -105,7 +111,7 @@ SocketIpStatus TcpServerSocket::openProtocol(SocketDescriptor& socketDescriptor)
|
||||
// TCP requires accepting on the socket to get the client socket file descriptor.
|
||||
clientFd = ::accept(serverFd, nullptr, nullptr);
|
||||
if (clientFd < 0) {
|
||||
return SOCK_FAILED_TO_ACCEPT; // What we have here is a failure to communicate
|
||||
return SOCK_FAILED_TO_ACCEPT; // What we have here is a failure to communicate
|
||||
}
|
||||
// Setup client send timeouts
|
||||
if (IpSocket::setupTimeouts(clientFd) != SOCK_SUCCESS) {
|
||||
@ -118,15 +124,19 @@ SocketIpStatus TcpServerSocket::openProtocol(SocketDescriptor& socketDescriptor)
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
I32 TcpServerSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||
return static_cast<I32>(::send(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS));
|
||||
FwSignedSizeType TcpServerSocket::sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) {
|
||||
return static_cast<FwSignedSizeType>(
|
||||
::send(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS));
|
||||
}
|
||||
|
||||
I32 TcpServerSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
|
||||
I32 size_buf;
|
||||
FwSignedSizeType TcpServerSocket::recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) {
|
||||
// recv will return 0 if the client has done an orderly shutdown
|
||||
size_buf = static_cast<I32>(::recv(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS));
|
||||
return size_buf;
|
||||
return static_cast<FwSignedSizeType>(
|
||||
::recv(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS));
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
#ifndef DRV_TCPSERVER_TCPHELPER_HPP_
|
||||
#define DRV_TCPSERVER_TCPHELPER_HPP_
|
||||
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <config/IpCfg.hpp>
|
||||
|
||||
namespace Drv {
|
||||
@ -76,7 +76,9 @@ class TcpServerSocket : public IpSocket {
|
||||
* \param size: size of data to send
|
||||
* \return: size of data sent, or -1 on error.
|
||||
*/
|
||||
I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||
FwSignedSizeType sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) override;
|
||||
/**
|
||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||
* \param socketDescriptor: descriptor to recv from
|
||||
@ -84,10 +86,9 @@ class TcpServerSocket : public IpSocket {
|
||||
* \param size: size of data buffer
|
||||
* \return: size of data received, or -1 on error.
|
||||
*/
|
||||
I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) override;
|
||||
|
||||
|
||||
|
||||
FwSignedSizeType recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) override;
|
||||
};
|
||||
} // namespace Drv
|
||||
|
||||
|
||||
@ -10,32 +10,32 @@
|
||||
//
|
||||
// ======================================================================
|
||||
#include <Drv/Ip/UdpSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Fw/Types/Assert.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Types/StringUtils.hpp>
|
||||
|
||||
#ifdef TGT_OS_TYPE_VXWORKS
|
||||
#include <socket.h>
|
||||
#include <inetLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <sockLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <sysLib.h>
|
||||
#include <errnoLib.h>
|
||||
#include <cstring>
|
||||
#include <errnoLib.h>
|
||||
#include <fioLib.h>
|
||||
#include <hostLib.h>
|
||||
#include <inetLib.h>
|
||||
#include <ioLib.h>
|
||||
#include <sockLib.h>
|
||||
#include <socket.h>
|
||||
#include <sysLib.h>
|
||||
#include <taskLib.h>
|
||||
#include <vxWorks.h>
|
||||
#include <cstring>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <cerrno>
|
||||
|
||||
namespace Drv {
|
||||
|
||||
@ -46,13 +46,18 @@ UdpSocket::UdpSocket() : IpSocket(), m_recv_configured(false) {
|
||||
|
||||
UdpSocket::~UdpSocket() = default;
|
||||
|
||||
SocketIpStatus UdpSocket::configure(const char* const hostname, const U16 port, const U32 timeout_seconds, const U32 timeout_microseconds) {
|
||||
FW_ASSERT(0); // Must use configureSend and/or configureRecv
|
||||
SocketIpStatus UdpSocket::configure(const char* const hostname,
|
||||
const U16 port,
|
||||
const U32 timeout_seconds,
|
||||
const U32 timeout_microseconds) {
|
||||
FW_ASSERT(0); // Must use configureSend and/or configureRecv
|
||||
return SocketIpStatus::SOCK_INVALID_CALL;
|
||||
}
|
||||
|
||||
|
||||
SocketIpStatus UdpSocket::configureSend(const char* const hostname, const U16 port, const U32 timeout_seconds, const U32 timeout_microseconds) {
|
||||
SocketIpStatus UdpSocket::configureSend(const char* const hostname,
|
||||
const U16 port,
|
||||
const U32 timeout_seconds,
|
||||
const U32 timeout_microseconds) {
|
||||
FW_ASSERT(hostname != nullptr);
|
||||
FW_ASSERT(this->isValidPort(port));
|
||||
FW_ASSERT(timeout_microseconds < 1000000);
|
||||
@ -63,18 +68,18 @@ SocketIpStatus UdpSocket::configureRecv(const char* hostname, const U16 port) {
|
||||
FW_ASSERT(hostname != nullptr);
|
||||
FW_ASSERT(this->isValidPort(port));
|
||||
FW_ASSERT(Fw::StringUtils::string_length(hostname, SOCKET_MAX_HOSTNAME_SIZE) < SOCKET_MAX_HOSTNAME_SIZE);
|
||||
|
||||
|
||||
// Initialize the receive address structure
|
||||
(void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
|
||||
m_addr_recv.sin_family = AF_INET;
|
||||
m_addr_recv.sin_port = htons(port);
|
||||
|
||||
|
||||
// Convert hostname to IP address
|
||||
SocketIpStatus status = IpSocket::addressToIp4(hostname, &m_addr_recv.sin_addr);
|
||||
if (status != SOCK_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
this->m_recv_configured = true;
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
@ -86,7 +91,7 @@ U16 UdpSocket::getRecvPort() {
|
||||
SocketIpStatus UdpSocket::bind(const int fd) {
|
||||
FW_ASSERT(fd != -1);
|
||||
struct sockaddr_in address = this->m_addr_recv;
|
||||
|
||||
|
||||
// OS specific settings
|
||||
#if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
|
||||
address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
|
||||
@ -97,25 +102,24 @@ SocketIpStatus UdpSocket::bind(const int fd) {
|
||||
}
|
||||
|
||||
socklen_t size = sizeof(address);
|
||||
if (::getsockname(fd, reinterpret_cast<struct sockaddr *>(&address), &size) == -1) {
|
||||
if (::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size) == -1) {
|
||||
return SOCK_FAILED_TO_READ_BACK_PORT;
|
||||
}
|
||||
|
||||
// Update m_addr_recv with the actual port assigned (for ephemeral port support)
|
||||
this->m_addr_recv.sin_port = address.sin_port;
|
||||
|
||||
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||
if (this->m_port == 0 && !this->m_recv_configured) {
|
||||
return SOCK_INVALID_CALL; // Neither send nor receive is configured
|
||||
return SOCK_INVALID_CALL; // Neither send nor receive is configured
|
||||
}
|
||||
|
||||
|
||||
SocketIpStatus status = SOCK_SUCCESS;
|
||||
int socketFd = -1;
|
||||
|
||||
|
||||
// Initialize address structure to zero before use
|
||||
struct sockaddr_in address;
|
||||
(void)::memset(&address, 0, sizeof(address));
|
||||
@ -147,6 +151,11 @@ SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||
return status;
|
||||
};
|
||||
|
||||
if (IpSocket::setupSocketOptions(socketFd) != SOCK_SUCCESS) {
|
||||
::close(socketFd);
|
||||
return SOCK_FAILED_TO_SET_SOCKET_OPTIONS;
|
||||
}
|
||||
|
||||
// Now apply timeouts
|
||||
status = this->setupTimeouts(socketFd);
|
||||
if (status != SOCK_SUCCESS) {
|
||||
@ -163,7 +172,7 @@ SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||
status = this->bind(socketFd);
|
||||
|
||||
if (status != SOCK_SUCCESS) {
|
||||
(void) ::close(socketFd); // Closing FD as a retry will reopen send side
|
||||
(void)::close(socketFd); // Closing FD as a retry will reopen send side
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@ -172,9 +181,9 @@ SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||
char recv_addr[INET_ADDRSTRLEN];
|
||||
const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
|
||||
if (recv_addr_str == nullptr) {
|
||||
(void) Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
|
||||
(void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
|
||||
if ((port == 0) && (recv_port > 0)) {
|
||||
Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
|
||||
} else if ((port > 0) && (recv_port == 0)) {
|
||||
@ -182,33 +191,39 @@ SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||
} else if ((port > 0) && (recv_port > 0)) {
|
||||
Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port, m_hostname, port);
|
||||
}
|
||||
|
||||
|
||||
FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
|
||||
socketDescriptor.fd = socketFd;
|
||||
return status;
|
||||
}
|
||||
|
||||
I32 UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||
FW_ASSERT(this->m_addr_send.sin_family != 0); // Make sure the address was previously setup
|
||||
FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
|
||||
FW_ASSERT(data != nullptr); // Data pointer should not be null
|
||||
|
||||
return static_cast<I32>(::sendto(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS,
|
||||
reinterpret_cast<struct sockaddr *>(&this->m_addr_send), sizeof(this->m_addr_send)));
|
||||
FwSignedSizeType UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) {
|
||||
FW_ASSERT(this->m_addr_send.sin_family != 0); // Make sure the address was previously setup
|
||||
FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
|
||||
FW_ASSERT(data != nullptr); // Data pointer should not be null
|
||||
|
||||
return static_cast<FwSignedSizeType>(
|
||||
::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
|
||||
reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
|
||||
}
|
||||
|
||||
I32 UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
|
||||
FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
|
||||
FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
|
||||
FW_ASSERT(data != nullptr); // Data pointer should not be null
|
||||
FwSignedSizeType UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) {
|
||||
FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
|
||||
FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
|
||||
FW_ASSERT(data != nullptr); // Data pointer should not be null
|
||||
|
||||
// Initialize sender address structure to zero
|
||||
struct sockaddr_in sender_addr;
|
||||
(void)::memset(&sender_addr, 0, sizeof(sender_addr));
|
||||
|
||||
socklen_t sender_addr_len = sizeof(sender_addr);
|
||||
I32 received = static_cast<I32>(::recvfrom(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS,
|
||||
reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
|
||||
FwSignedSizeType received = static_cast<FwSignedSizeType>(
|
||||
::recvfrom(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS,
|
||||
reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
|
||||
// If we have not configured a send port, set it to the source of the last received packet
|
||||
if (received >= 0 && this->m_addr_send.sin_port == 0) {
|
||||
this->m_addr_send = sender_addr;
|
||||
@ -218,21 +233,21 @@ I32 UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const
|
||||
return received;
|
||||
}
|
||||
|
||||
SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||
SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
|
||||
// Note: socketDescriptor.fd can be -1 in some test cases
|
||||
FW_ASSERT((size == 0) || (data != nullptr));
|
||||
|
||||
|
||||
// Special case for zero-length datagrams in UDP
|
||||
if (size == 0) {
|
||||
errno = 0;
|
||||
I32 sent = this->sendProtocol(socketDescriptor, data, 0);
|
||||
FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
|
||||
if (sent == -1) {
|
||||
if (errno == EINTR) {
|
||||
// For zero-length datagrams, we'll just try once more if interrupted
|
||||
errno = 0;
|
||||
sent = this->sendProtocol(socketDescriptor, data, 0);
|
||||
}
|
||||
|
||||
|
||||
if (sent == -1) {
|
||||
if ((errno == EBADF) || (errno == ECONNRESET)) {
|
||||
return SOCK_DISCONNECTED;
|
||||
@ -244,7 +259,7 @@ SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U
|
||||
// For zero-length datagrams in UDP, success is either 0 or a non-negative value
|
||||
return SOCK_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// For non-zero-length data, delegate to the base class implementation
|
||||
return IpSocket::send(socketDescriptor, data, size);
|
||||
}
|
||||
|
||||
@ -12,17 +12,25 @@
|
||||
#ifndef DRV_IP_UDPSOCKET_HPP_
|
||||
#define DRV_IP_UDPSOCKET_HPP_
|
||||
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <config/IpCfg.hpp>
|
||||
|
||||
// Include system headers for sockaddr_in
|
||||
#ifdef TGT_OS_TYPE_VXWORKS
|
||||
#include <socket.h>
|
||||
#include <inetLib.h>
|
||||
#include <inetLib.h>
|
||||
#include <socket.h>
|
||||
|
||||
// Undefine these Vxworks-defined macros because
|
||||
// they collide with member variables in F Prime.
|
||||
// These macros are defined somewhere in inetLib.h.
|
||||
#undef m_type
|
||||
#undef m_data
|
||||
#undef m_len
|
||||
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
namespace Drv {
|
||||
@ -49,7 +57,9 @@ class UdpSocket : public IpSocket {
|
||||
*
|
||||
* \warning configure is disabled for UdpSocket. Use configureSend and configureRecv instead.
|
||||
*/
|
||||
SocketIpStatus configure(const char* hostname, const U16 port, const U32 send_timeout_seconds,
|
||||
SocketIpStatus configure(const char* hostname,
|
||||
const U16 port,
|
||||
const U32 send_timeout_seconds,
|
||||
const U32 send_timeout_microseconds) override;
|
||||
|
||||
/**
|
||||
@ -70,7 +80,9 @@ class UdpSocket : public IpSocket {
|
||||
* \param send_timeout_microseconds: send timeout microseconds portion. Must be less than 1000000
|
||||
* \return status of configure
|
||||
*/
|
||||
SocketIpStatus configureSend(const char* hostname, const U16 port, const U32 send_timeout_seconds,
|
||||
SocketIpStatus configureSend(const char* hostname,
|
||||
const U16 port,
|
||||
const U32 send_timeout_seconds,
|
||||
const U32 send_timeout_microseconds);
|
||||
|
||||
/**
|
||||
@ -105,7 +117,7 @@ class UdpSocket : public IpSocket {
|
||||
* \param size: size of data to send
|
||||
* \return: status of the send operation
|
||||
*/
|
||||
SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||
SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
@ -127,7 +139,9 @@ class UdpSocket : public IpSocket {
|
||||
* \param size: size of data to send
|
||||
* \return: size of data sent, or -1 on error.
|
||||
*/
|
||||
I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||
FwSignedSizeType sendProtocol(const SocketDescriptor& socketDescriptor,
|
||||
const U8* const data,
|
||||
const FwSizeType size) override;
|
||||
/**
|
||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||
* \param socketDescriptor: descriptor to recv from
|
||||
@ -135,7 +149,9 @@ class UdpSocket : public IpSocket {
|
||||
* \param size: size of data buffer
|
||||
* \return: size of data received, or -1 on error.
|
||||
*/
|
||||
I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) override;
|
||||
FwSignedSizeType recvProtocol(const SocketDescriptor& socketDescriptor,
|
||||
U8* const data,
|
||||
const FwSizeType size) override;
|
||||
/**
|
||||
* \brief Handle zero return from recvProtocol for UDP
|
||||
*
|
||||
@ -149,7 +165,7 @@ class UdpSocket : public IpSocket {
|
||||
private:
|
||||
struct sockaddr_in m_addr_send; //!< UDP server address for sending
|
||||
struct sockaddr_in m_addr_recv; //!< UDP server address for receiving
|
||||
bool m_recv_configured; //!< True if configureRecv was called
|
||||
bool m_recv_configured; //!< True if configureRecv was called
|
||||
};
|
||||
} // namespace Drv
|
||||
|
||||
|
||||
@ -154,11 +154,11 @@ socketBoth.configureRecv(127.0.0.1, 60212);
|
||||
```
|
||||
### Support for Ephemeral Ports
|
||||
|
||||
Drv::UdpSocket supports ephemeral ports through passing a 0 as the port argument for either `Drv::UdpSocket::configureSend`
|
||||
Drv::UdpSocket supports ephemeral ports through passing a 0 as the port argument for either `Drv::UdpSocket::configureSend`
|
||||
or `Drv::UdpSocket::configureRecv`.
|
||||
|
||||
For `Drv::UdpSocket::configureSend` this means that you would like to set up the UdpSocket to be able to respond to the source
|
||||
port that is indicated in the UDP datagrams you receive. Note that this configuration will set up the send port to the port
|
||||
For `Drv::UdpSocket::configureSend` this means that you would like to set up the UdpSocket to be able to respond to the source
|
||||
port that is indicated in the UDP datagrams you receive. Note that this configuration will set up the send port to the port
|
||||
specified only in the first message received.
|
||||
|
||||
For `Drv::UdpSocket::configureRecv` this means that you would like to be assigned an ephemeral port. This would generally be used
|
||||
@ -168,16 +168,17 @@ for setting up a sender that would like to receive responses to messages on an e
|
||||
|
||||
The Drv::SocketComponentHelper is intended as a base class used to add in the functionality of an automatically reconnecting
|
||||
receive thread to another class (typically an F´ component) as well as an interface between the component using an IP socket
|
||||
and the IP socket library functions implemented in this folder. In order for this thread to function, the inheritor must
|
||||
and the IP socket library functions implemented in this folder. In order for the receive thread to function, the inheritor must
|
||||
implement several methods to provide the necessary interaction of this thread. These functions are described in the next
|
||||
section.
|
||||
section. The automatic reconnection is handled by a separate dedicated thread upon request from either the receive thread
|
||||
or a send request.
|
||||
|
||||
In order to start the receiving thread a call to the `Drv::SocketComponentHelper::start` method is performed passing
|
||||
in a name, and all arguments to `Os::Task::start` to start the task. An optional parameter, reconnect, will determine if
|
||||
this read task will reconnect to sockets should a disconnect or error occur. Once started the read task will continue
|
||||
in a name, and all arguments to `Os::Task::start` to start the receive and reconnect tasks. An optional parameter, reconnect,
|
||||
will determine if this read task will request reconnects to sockets should a disconnect or error occur. Once started, the read task will continue
|
||||
until a `Drv::SocketComponentHelper::stop` has been called or an error occurred when started without reconnect set to
|
||||
`true`. Once the socket stop call has been made, the user should call `Drv::SocketComponentHelper::join` in order to
|
||||
wait until the full task has finished. `Drv::SocketComponentHelper::stop` will call `Drv::SocketComponentHelper::close` on the
|
||||
wait until the full tasks have finished. `Drv::SocketComponentHelper::stop` will call `Drv::SocketComponentHelper::close` on the
|
||||
provided Drv::IpSocket to ensure that any blocking reads exit freeing the thread to completely stop. Normal usage of
|
||||
a Drv::SocketComponentHelper derived class is shown below.
|
||||
|
||||
@ -187,7 +188,7 @@ uplinkComm.start(name); // Default reconnect=true
|
||||
...
|
||||
|
||||
uplinkComm.stop();
|
||||
(void) uplinkComm.join();
|
||||
(void) uplinkComm.join(); // this will join the receive and reconnect tasks
|
||||
```
|
||||
|
||||
`Drv::SocketComponentHelper::open` and `Drv::SocketComponentHelper::close` convenience methods are also provided to open and close the
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
//
|
||||
|
||||
#include "PortSelector.hpp"
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
namespace Drv {
|
||||
namespace Test {
|
||||
@ -29,19 +29,18 @@ U16 get_free_port(bool udp) {
|
||||
};
|
||||
|
||||
// When we are setting up for receiving as well, then we must bind to a port
|
||||
if (::bind(socketFd, reinterpret_cast<struct sockaddr *>(&address), sizeof(address)) == -1) {
|
||||
if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) == -1) {
|
||||
::close(socketFd);
|
||||
return 0;
|
||||
}
|
||||
socklen_t size = sizeof(address);
|
||||
if (::getsockname(socketFd, reinterpret_cast<struct sockaddr *>(&address), &size) == -1) {
|
||||
if (::getsockname(socketFd, reinterpret_cast<struct sockaddr*>(&address), &size) == -1) {
|
||||
::close(socketFd);
|
||||
return 0;
|
||||
}
|
||||
U16 port = ntohs(address.sin_port);
|
||||
::close(socketFd); // Close this recursion's port again, such that we don't infinitely loop
|
||||
::close(socketFd); // Close this recursion's port again, such that we don't infinitely loop
|
||||
return port;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Test
|
||||
} // namespace Drv
|
||||
|
||||
@ -23,6 +23,6 @@ namespace Test {
|
||||
* \return 0 on error, or a free port on success
|
||||
*/
|
||||
U16 get_free_port(bool is_udp = false);
|
||||
}
|
||||
}
|
||||
} // namespace Test
|
||||
} // namespace Drv
|
||||
#endif // DRV_TEST_PORTSELECTOR_HPP
|
||||
|
||||
@ -1,52 +1,52 @@
|
||||
//
|
||||
// Created by mstarch on 12/10/20.
|
||||
//
|
||||
#include <Os/Task.hpp>
|
||||
#include <Drv/Ip/test/ut/SocketTestHelper.hpp>
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <Drv/Ip/test/ut/SocketTestHelper.hpp>
|
||||
#include <Os/Task.hpp>
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <arpa/inet.h>
|
||||
#include <config/IpCfg.hpp>
|
||||
|
||||
namespace Drv {
|
||||
namespace Test {
|
||||
|
||||
const U32 MAX_DRV_TEST_MESSAGE_SIZE = 1024;
|
||||
const FwSizeType MAX_DRV_TEST_MESSAGE_SIZE = 1024;
|
||||
|
||||
void force_recv_timeout(int fd, Drv::IpSocket& socket) {
|
||||
// Set timeout socket option
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 50; // 50ms max before test failure
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&timeout), sizeof(timeout));
|
||||
timeout.tv_usec = 50; // 50ms max before test failure
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
|
||||
}
|
||||
|
||||
void validate_random_data(U8 *data, U8 *truth, FwSizeType size) {
|
||||
void validate_random_data(U8* data, U8* truth, FwSizeType size) {
|
||||
for (FwSizeType i = 0; i < size; i++) {
|
||||
ASSERT_EQ(data[i], truth[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void fill_random_data(U8 *data, FwSizeType size) {
|
||||
void fill_random_data(U8* data, FwSizeType size) {
|
||||
ASSERT_NE(size, 0u) << "Trying to fill random data of size 0";
|
||||
for (FwSizeType i = 0; i < size; i++) {
|
||||
data[i] = static_cast<U8>(STest::Pick::any());
|
||||
}
|
||||
}
|
||||
|
||||
void validate_random_buffer(Fw::Buffer &buffer, U8 *data) {
|
||||
void validate_random_buffer(Fw::Buffer& buffer, U8* data) {
|
||||
validate_random_data(buffer.getData(), data, buffer.getSize());
|
||||
buffer.setSize(0);
|
||||
}
|
||||
|
||||
U32 fill_random_buffer(Fw::Buffer &buffer) {
|
||||
FwSizeType fill_random_buffer(Fw::Buffer& buffer) {
|
||||
buffer.setSize(static_cast<FwSizeType>(STest::Pick::lowerUpper(1, static_cast<U32>(buffer.getSize()))));
|
||||
fill_random_data(buffer.getData(), buffer.getSize());
|
||||
return static_cast<U32>(buffer.getSize());
|
||||
return buffer.getSize();
|
||||
}
|
||||
|
||||
void drain(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd) {
|
||||
@ -54,18 +54,18 @@ void drain(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd) {
|
||||
// Drain the server in preparation for close
|
||||
while (status == Drv::SOCK_SUCCESS || status == Drv::SOCK_NO_DATA_AVAILABLE) {
|
||||
U8 buffer[1];
|
||||
U32 size = sizeof buffer;
|
||||
FwSizeType size = sizeof buffer;
|
||||
status = receiver.recv(receiver_fd, buffer, size);
|
||||
}
|
||||
ASSERT_EQ(status, Drv::SocketIpStatus::SOCK_DISCONNECTED) << "Socket did not disconnect as expected";
|
||||
}
|
||||
|
||||
void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8* buffer, U32 size) {
|
||||
void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8* buffer, FwSizeType size) {
|
||||
ASSERT_NE(buffer, nullptr);
|
||||
U32 received_size = 0;
|
||||
FwSizeType received_size = 0;
|
||||
Drv::SocketIpStatus status;
|
||||
do {
|
||||
U32 size_in_out = size - received_size;
|
||||
FwSizeType size_in_out = size - received_size;
|
||||
status = receiver.recv(receiver_fd, buffer + received_size, size_in_out);
|
||||
ASSERT_TRUE((status == Drv::SOCK_NO_DATA_AVAILABLE || status == Drv::SOCK_SUCCESS));
|
||||
received_size += size_in_out;
|
||||
@ -73,8 +73,11 @@ void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8
|
||||
EXPECT_EQ(received_size, size);
|
||||
}
|
||||
|
||||
void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, Drv::SocketDescriptor& sender_fd, Drv::SocketDescriptor& receiver_fd) {
|
||||
U32 size = MAX_DRV_TEST_MESSAGE_SIZE;
|
||||
void send_recv(Drv::IpSocket& sender,
|
||||
Drv::IpSocket& receiver,
|
||||
Drv::SocketDescriptor& sender_fd,
|
||||
Drv::SocketDescriptor& receiver_fd) {
|
||||
FwSizeType size = MAX_DRV_TEST_MESSAGE_SIZE;
|
||||
|
||||
U8 buffer_out[MAX_DRV_TEST_MESSAGE_SIZE] = {0};
|
||||
U8 buffer_in[MAX_DRV_TEST_MESSAGE_SIZE] = {0};
|
||||
@ -91,5 +94,5 @@ U64 get_configured_delay_ms() {
|
||||
(static_cast<U64>(SOCKET_RETRY_INTERVAL.getUSeconds()) / 1000);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace Test
|
||||
} // namespace Drv
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
//
|
||||
// Created by mstarch on 12/10/20.
|
||||
//
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw//Buffer/Buffer.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Fw/Buffer/Buffer.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
|
||||
#ifndef DRV_TEST_SOCKETHELPER_HPP
|
||||
#define DRV_TEST_SOCKETHELPER_HPP
|
||||
@ -19,7 +19,7 @@ static constexpr U16 MAX_ITER = 10;
|
||||
* @param fd: socket file descriptor
|
||||
* @param socket: socket to make timeout
|
||||
*/
|
||||
void force_recv_timeout(int fd, Drv::IpSocket &socket);
|
||||
void force_recv_timeout(int fd, Drv::IpSocket& socket);
|
||||
|
||||
/**
|
||||
* Validate random data from data against truth
|
||||
@ -27,27 +27,27 @@ void force_recv_timeout(int fd, Drv::IpSocket &socket);
|
||||
* @param truth: truth data to validate
|
||||
* @param size: size to validate
|
||||
*/
|
||||
void validate_random_data(U8 *data, U8 *truth, FwSizeType size);
|
||||
void validate_random_data(U8* data, U8* truth, FwSizeType size);
|
||||
|
||||
/**
|
||||
* Fills in the given data buffer with randomly picked data.
|
||||
* @param data: data to file
|
||||
* @param size: size of fill
|
||||
*/
|
||||
void fill_random_data(U8 *data, FwSizeType size);
|
||||
void fill_random_data(U8* data, FwSizeType size);
|
||||
|
||||
/**
|
||||
* Validates a given buffer against the data provided.
|
||||
* @param buffer: buffer to validate
|
||||
* @param truth: correct data to validate against
|
||||
*/
|
||||
void validate_random_buffer(Fw::Buffer &buffer, U8 *data);
|
||||
void validate_random_buffer(Fw::Buffer& buffer, U8* data);
|
||||
|
||||
/**
|
||||
* Fill random data into the buffer (using a random length).
|
||||
* @param buffer: buffer to fill.
|
||||
*/
|
||||
U32 fill_random_buffer(Fw::Buffer &buffer);
|
||||
FwSizeType fill_random_buffer(Fw::Buffer& buffer);
|
||||
|
||||
/**
|
||||
* Send/receive pair.
|
||||
@ -56,7 +56,10 @@ U32 fill_random_buffer(Fw::Buffer &buffer);
|
||||
* @param sender_fd: file descriptor for sender
|
||||
* @param receiver_fd: file descriptor for receiver
|
||||
*/
|
||||
void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, Drv::SocketDescriptor& sender_fd, Drv::SocketDescriptor& receiver_fd);
|
||||
void send_recv(Drv::IpSocket& sender,
|
||||
Drv::IpSocket& receiver,
|
||||
Drv::SocketDescriptor& sender_fd,
|
||||
Drv::SocketDescriptor& receiver_fd);
|
||||
|
||||
/**
|
||||
* Drain bytes from the socket until disconnect received.
|
||||
@ -72,17 +75,17 @@ void drain(Drv::IpSocket& receiver, Drv::SocketDescriptor& drain_fd);
|
||||
* @param buffer: buffer
|
||||
* @param size: size to receive
|
||||
*/
|
||||
void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8* buffer, U32 size);
|
||||
void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8* buffer, FwSizeType size);
|
||||
|
||||
/**
|
||||
* Wait on socket change.
|
||||
*/
|
||||
bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations);
|
||||
bool wait_on_change(Drv::IpSocket& socket, bool open, U32 iterations);
|
||||
|
||||
/**
|
||||
* Wait on started
|
||||
*/
|
||||
bool wait_on_started(Drv::IpSocket &socket, bool open, U32 iterations);
|
||||
*/
|
||||
bool wait_on_started(Drv::IpSocket& socket, bool open, U32 iterations);
|
||||
|
||||
/**
|
||||
* Get the configured delay, converted to milliseconds
|
||||
@ -90,6 +93,6 @@ bool wait_on_started(Drv::IpSocket &socket, bool open, U32 iterations);
|
||||
*/
|
||||
U64 get_configured_delay_ms();
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace Test
|
||||
} // namespace Drv
|
||||
#endif
|
||||
|
||||
@ -2,22 +2,21 @@
|
||||
// Created by mstarch on 12/7/20.
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <Drv/Ip/TcpClientSocket.hpp>
|
||||
#include <Drv/Ip/TcpServerSocket.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Drv/Ip/SocketComponentHelper.hpp>
|
||||
#include <Os/Console.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Drv/Ip/TcpClientSocket.hpp>
|
||||
#include <Drv/Ip/TcpServerSocket.hpp>
|
||||
#include <Drv/Ip/test/ut/SocketTestHelper.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Os/Console.hpp>
|
||||
|
||||
Os::Console logger;
|
||||
|
||||
|
||||
void test_with_loop(U32 iterations) {
|
||||
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
||||
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
||||
|
||||
U16 port = 0; // Choose a port
|
||||
U16 port = 0; // Choose a port
|
||||
Drv::TcpServerSocket server;
|
||||
Drv::SocketDescriptor server_fd;
|
||||
Drv::SocketDescriptor client_fd;
|
||||
@ -28,7 +27,7 @@ void test_with_loop(U32 iterations) {
|
||||
// Loop through a bunch of client disconnects
|
||||
for (U32 i = 0; i < iterations; i++) {
|
||||
Drv::TcpClientSocket client;
|
||||
client.configure("127.0.0.1", server.getListenPort(),0,100);
|
||||
client.configure("127.0.0.1", server.getListenPort(), 0, 100);
|
||||
// client_fd gets assigned a real value here
|
||||
status1 = client.open(client_fd);
|
||||
EXPECT_EQ(status1, Drv::SOCK_SUCCESS) << "With errno: " << errno;
|
||||
@ -37,7 +36,6 @@ void test_with_loop(U32 iterations) {
|
||||
status2 = server.open(server_fd);
|
||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
||||
|
||||
|
||||
// If all the opens worked, then run this
|
||||
if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) {
|
||||
// Force the sockets not to hang, if at all possible
|
||||
@ -55,7 +53,6 @@ void test_with_loop(U32 iterations) {
|
||||
server.terminate(server_fd);
|
||||
}
|
||||
|
||||
|
||||
TEST(Nominal, TestNominalTcp) {
|
||||
test_with_loop(1);
|
||||
}
|
||||
|
||||
@ -2,24 +2,20 @@
|
||||
// Created by mstarch on 12/7/20.
|
||||
//
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <Drv/Ip/UdpSocket.hpp>
|
||||
#include <Drv/Ip/IpSocket.hpp>
|
||||
#include <Os/Console.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Drv/Ip/UdpSocket.hpp>
|
||||
#include <Drv/Ip/test/ut/PortSelector.hpp>
|
||||
#include <Drv/Ip/test/ut/SocketTestHelper.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include <Os/Console.hpp>
|
||||
|
||||
Os::Console logger;
|
||||
|
||||
enum UdpMode {
|
||||
DUPLEX,
|
||||
SEND,
|
||||
RECEIVE
|
||||
};
|
||||
enum UdpMode { DUPLEX, SEND, RECEIVE };
|
||||
|
||||
void test_with_loop(U32 iterations, UdpMode udp_mode) {
|
||||
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
||||
@ -106,34 +102,34 @@ TEST(UdpZeroLength, TestZeroLengthUdpDatagram) {
|
||||
Drv::SocketDescriptor recv_fd;
|
||||
U16 port = Drv::Test::get_free_port(true);
|
||||
ASSERT_NE(0, port);
|
||||
|
||||
|
||||
// Configure receiver and sender
|
||||
ASSERT_EQ(receiver.configureRecv("127.0.0.1", port), Drv::SOCK_SUCCESS);
|
||||
ASSERT_EQ(receiver.open(recv_fd), Drv::SOCK_SUCCESS);
|
||||
|
||||
|
||||
ASSERT_EQ(sender.configureSend("127.0.0.1", port, 1, 0), Drv::SOCK_SUCCESS);
|
||||
ASSERT_EQ(sender.open(send_fd), Drv::SOCK_SUCCESS);
|
||||
|
||||
// Send a zero-length datagram using the F' socket wrapper
|
||||
U8 empty_data[1] = {0}; // Buffer is required, but size is 0
|
||||
ASSERT_EQ(sender.send(send_fd, empty_data, 0), Drv::SOCK_SUCCESS)
|
||||
U8 empty_data[1] = {0}; // Buffer is required, but size is 0
|
||||
ASSERT_EQ(sender.send(send_fd, empty_data, 0), Drv::SOCK_SUCCESS)
|
||||
<< "Failed to send zero-length datagram using F' socket wrapper";
|
||||
|
||||
// Add a small delay to ensure the packet has time to be processed by the OS
|
||||
usleep(10000); // 10ms delay
|
||||
usleep(10000); // 10ms delay
|
||||
|
||||
// Receive the zero-length datagram using the F' socket wrapper
|
||||
U8 recv_buf[1] = {0xFF};
|
||||
U32 recv_buf_len = 1;
|
||||
I32 recv_status = receiver.recv(recv_fd, recv_buf, recv_buf_len);
|
||||
FwSizeType recv_buf_len = 1;
|
||||
FwSignedSizeType recv_status = receiver.recv(recv_fd, recv_buf, recv_buf_len);
|
||||
|
||||
// Expect 0 (success) for a zero-length datagram.
|
||||
ASSERT_EQ(recv_status, 0)
|
||||
<< "Expected recv_status 0 for zero-length datagram, but got " << recv_status << " with errno=" << errno;
|
||||
|
||||
ASSERT_EQ(recv_status, 0) << "Expected recv_status 0 for zero-length datagram, but got " << recv_status
|
||||
<< " with errno=" << errno;
|
||||
|
||||
// Check that the received length is reported as 0
|
||||
ASSERT_EQ(recv_buf_len, 0) << "Expected received length 0, but got " << recv_buf_len;
|
||||
|
||||
|
||||
// Check that the received buffer is unchanged meaning no data was received
|
||||
ASSERT_EQ(recv_buf[0], 0xFF) << "Expected unchanged buffer (0xFF), but got " << recv_buf[0];
|
||||
|
||||
@ -163,23 +159,23 @@ TEST(Ephemeral, TestEphemeralPorts) {
|
||||
|
||||
// Send a test message
|
||||
const char* msg = "hello from ephemeral sender";
|
||||
U32 msg_len = static_cast<U32>(strlen(msg) + 1);
|
||||
FwSizeType msg_len = static_cast<FwSizeType>(strlen(msg) + 1);
|
||||
ASSERT_EQ(sender.send(send_fd, reinterpret_cast<const U8*>(msg), msg_len), Drv::SOCK_SUCCESS);
|
||||
|
||||
// Receive the message and capture sender's port
|
||||
char recv_buf[64] = {0};
|
||||
U32 recv_buf_len = sizeof(recv_buf);
|
||||
FwSizeType recv_buf_len = sizeof(recv_buf);
|
||||
ASSERT_EQ(receiver.recv(recv_fd, reinterpret_cast<U8*>(recv_buf), recv_buf_len), Drv::SOCK_SUCCESS);
|
||||
ASSERT_STREQ(msg, recv_buf);
|
||||
|
||||
// Receiver sends a response back to sender
|
||||
const char* reply = "reply from receiver";
|
||||
U32 reply_len = static_cast<U32>(strlen(reply) + 1);
|
||||
FwSizeType reply_len = static_cast<FwSizeType>(strlen(reply) + 1);
|
||||
ASSERT_EQ(receiver.send(recv_fd, reinterpret_cast<const U8*>(reply), reply_len), Drv::SOCK_SUCCESS);
|
||||
|
||||
// Sender receives the response
|
||||
char reply_buf[64] = {0};
|
||||
U32 reply_buf_len = sizeof(reply_buf);
|
||||
FwSizeType reply_buf_len = sizeof(reply_buf);
|
||||
ASSERT_EQ(sender.recv(send_fd, reinterpret_cast<U8*>(reply_buf), reply_buf_len), Drv::SOCK_SUCCESS);
|
||||
ASSERT_STREQ(reply, reply_buf);
|
||||
|
||||
|
||||
@ -20,11 +20,11 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Drv {
|
||||
|
||||
|
||||
Os::File::Status errno_to_file_status(int errno_input) {
|
||||
Os::File::Status status = Os::File::Status::OTHER_ERROR;
|
||||
switch (errno_input) {
|
||||
@ -123,7 +123,7 @@ U32 configuration_to_event_flags(Drv::LinuxGpioDriver::GpioConfiguration configu
|
||||
}
|
||||
|
||||
LinuxGpioDriver ::~LinuxGpioDriver() {
|
||||
(void) ::close(this->m_fd);
|
||||
(void)::close(this->m_fd);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@ -138,7 +138,7 @@ Os::File::Status LinuxGpioDriver ::setupLineHandle(const int chip_descriptor,
|
||||
Os::File::Status status = Os::File::OP_OK;
|
||||
// Set up the GPIO request
|
||||
struct gpiohandle_request request;
|
||||
(void) ::memset(&request, 0, sizeof request);
|
||||
(void)::memset(&request, 0, sizeof request);
|
||||
request.lineoffsets[0] = gpio;
|
||||
Fw::StringUtils::string_copy(request.consumer_label, FW_OPTIONAL_NAME(this->getObjName()),
|
||||
static_cast<FwSizeType>(sizeof request.consumer_label));
|
||||
@ -164,7 +164,7 @@ Os::File::Status LinuxGpioDriver ::setupLineEvent(const int chip_descriptor,
|
||||
Os::File::Status status = Os::File::OP_OK;
|
||||
// Set up the GPIO request
|
||||
struct gpioevent_request event;
|
||||
(void) ::memset(&event, 0, sizeof event);
|
||||
(void)::memset(&event, 0, sizeof event);
|
||||
event.lineoffset = gpio;
|
||||
Fw::StringUtils::string_copy(event.consumer_label, FW_OPTIONAL_NAME(this->getObjName()),
|
||||
static_cast<FwSizeType>(sizeof event.consumer_label));
|
||||
@ -188,7 +188,8 @@ Os::File::Status LinuxGpioDriver ::open(const char* device,
|
||||
Os::File::Status status = Os::File::OP_OK;
|
||||
Os::File chip_file;
|
||||
FW_ASSERT(device != nullptr);
|
||||
FW_ASSERT(configuration < MAX_GPIO_CONFIGURATION and configuration >= 0, static_cast<FwAssertArgType>(configuration));
|
||||
FW_ASSERT(configuration < MAX_GPIO_CONFIGURATION and configuration >= 0,
|
||||
static_cast<FwAssertArgType>(configuration));
|
||||
|
||||
// Open chip file and check for success
|
||||
status = chip_file.open(device, Os::File::Mode::OPEN_WRITE);
|
||||
@ -197,10 +198,9 @@ Os::File::Status LinuxGpioDriver ::open(const char* device,
|
||||
return status;
|
||||
}
|
||||
// Read chip information and check for correctness
|
||||
int chip_descriptor =
|
||||
reinterpret_cast<Os::Posix::File::PosixFileHandle*>(chip_file.getHandle())->m_file_descriptor;
|
||||
int chip_descriptor = reinterpret_cast<Os::Posix::File::PosixFileHandle*>(chip_file.getHandle())->m_file_descriptor;
|
||||
struct gpiochip_info chip_info;
|
||||
(void) ::memset(&chip_info, 0, sizeof chip_info);
|
||||
(void)::memset(&chip_info, 0, sizeof chip_info);
|
||||
int return_value = ioctl(chip_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info);
|
||||
if (return_value != 0) {
|
||||
status = errno_to_file_status(errno);
|
||||
@ -215,13 +215,13 @@ Os::File::Status LinuxGpioDriver ::open(const char* device,
|
||||
}
|
||||
Fw::String pin_message("Unknown");
|
||||
struct gpioline_info pin_info;
|
||||
(void) ::memset(&pin_info, 0, sizeof pin_info);
|
||||
(void)::memset(&pin_info, 0, sizeof pin_info);
|
||||
pin_info.line_offset = gpio;
|
||||
return_value = ioctl(chip_descriptor, GPIO_GET_LINEINFO_IOCTL, &pin_info);
|
||||
if (return_value == 0) {
|
||||
const bool has_consumer = pin_info.consumer[0] != '\0';
|
||||
pin_message.format("%s%s%s", pin_info.name, has_consumer ? " with current consumer " : "",
|
||||
has_consumer ? pin_info.consumer : "");
|
||||
has_consumer ? pin_info.consumer : "");
|
||||
}
|
||||
|
||||
// Set up pin and set file descriptor for it
|
||||
@ -247,8 +247,7 @@ Os::File::Status LinuxGpioDriver ::open(const char* device,
|
||||
this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, pin_message,
|
||||
Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
|
||||
} else {
|
||||
this->log_DIAGNOSTIC_OpenChip(Fw::String(chip_info.name), Fw::String(chip_info.label),
|
||||
gpio, pin_message);
|
||||
this->log_DIAGNOSTIC_OpenChip(Fw::String(chip_info.name), Fw::String(chip_info.label), gpio, pin_message);
|
||||
this->m_fd = pin_fd;
|
||||
this->m_configuration = configuration;
|
||||
}
|
||||
@ -259,7 +258,7 @@ Drv::GpioStatus LinuxGpioDriver ::gpioRead_handler(const FwIndexType portNum, Fw
|
||||
Drv::GpioStatus status = Drv::GpioStatus::INVALID_MODE;
|
||||
if (this->m_configuration == GpioConfiguration::GPIO_INPUT) {
|
||||
struct gpiohandle_data values;
|
||||
(void) ::memset(&values, 0, sizeof values);
|
||||
(void)::memset(&values, 0, sizeof values);
|
||||
int return_value = ioctl(this->m_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values);
|
||||
if (return_value != 0) {
|
||||
status = errno_to_gpio_status(errno);
|
||||
@ -275,7 +274,7 @@ Drv::GpioStatus LinuxGpioDriver ::gpioWrite_handler(const FwIndexType portNum, c
|
||||
Drv::GpioStatus status = Drv::GpioStatus::INVALID_MODE;
|
||||
if (this->m_configuration == GpioConfiguration::GPIO_OUTPUT) {
|
||||
struct gpiohandle_data values;
|
||||
(void) ::memset(&values, 0, sizeof values);
|
||||
(void)::memset(&values, 0, sizeof values);
|
||||
values.values[0] = (state == Fw::Logic::HIGH) ? 1 : 0;
|
||||
int return_value = ioctl(this->m_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values);
|
||||
if (return_value != 0) {
|
||||
@ -291,16 +290,19 @@ void LinuxGpioDriver ::pollLoop() {
|
||||
// Ensure size of FwSizeType is large enough to fit the necessary ranges
|
||||
// NOTE: casts to unsigned types for int and ssize_t are made to avoid sign-compare warning;
|
||||
// in both cases the cast is safe because max() returns nonnegative value.
|
||||
static_assert(GPIO_POLL_TIMEOUT < static_cast<unsigned int>(std::numeric_limits<int>::max()), "Poll timeout would overflow");
|
||||
static_assert(GPIO_POLL_TIMEOUT < static_cast<unsigned int>(std::numeric_limits<int>::max()),
|
||||
"Poll timeout would overflow");
|
||||
static_assert(sizeof(struct gpioevent_data) < std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
|
||||
using unsigned_ssize_t = std::make_unsigned<ssize_t>::type;
|
||||
static_assert(static_cast<unsigned_ssize_t>(std::numeric_limits<ssize_t>::max()) <= std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
|
||||
static_assert(
|
||||
static_cast<unsigned_ssize_t>(std::numeric_limits<ssize_t>::max()) <= std::numeric_limits<FwSizeType>::max(),
|
||||
"FwSizeType too small");
|
||||
// Setup poll information
|
||||
pollfd file_descriptors[1];
|
||||
// Loop forever
|
||||
while (this->getRunning()) {
|
||||
// Setup polling
|
||||
(void) ::memset(file_descriptors, 0, sizeof file_descriptors);
|
||||
(void)::memset(file_descriptors, 0, sizeof file_descriptors);
|
||||
file_descriptors[0].fd = this->m_fd;
|
||||
file_descriptors[0].events = POLLIN; // Ask for read data available
|
||||
// Poll for fd bing ready
|
||||
|
||||
@ -10,119 +10,92 @@
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Drv/LinuxI2cDriver/LinuxI2cDriver.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Logger/Logger.hpp>
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
|
||||
#include <unistd.h> // required for I2C device access
|
||||
#include <fcntl.h> // required for I2C device configuration
|
||||
#include <sys/ioctl.h> // required for I2C device usage
|
||||
#include <linux/i2c.h> // required for struct / constant definitions
|
||||
#include <linux/i2c-dev.h> // required for constant definitions
|
||||
#include <fcntl.h> // required for I2C device configuration
|
||||
#include <linux/i2c-dev.h> // required for constant definitions
|
||||
#include <linux/i2c.h> // required for struct / constant definitions
|
||||
#include <sys/ioctl.h> // required for I2C device usage
|
||||
#include <unistd.h> // required for I2C device access
|
||||
#include <cerrno>
|
||||
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
LinuxI2cDriver ::
|
||||
LinuxI2cDriver(
|
||||
const char *const compName
|
||||
) : LinuxI2cDriverComponentBase(compName),
|
||||
m_fd(-1)
|
||||
{
|
||||
LinuxI2cDriver ::LinuxI2cDriver(const char* const compName) : LinuxI2cDriverComponentBase(compName), m_fd(-1) {}
|
||||
|
||||
}
|
||||
|
||||
LinuxI2cDriver::
|
||||
~LinuxI2cDriver()
|
||||
{
|
||||
if (-1 != this->m_fd) { // check if file is open
|
||||
::close(this->m_fd);
|
||||
LinuxI2cDriver::~LinuxI2cDriver() {
|
||||
if (-1 != this->m_fd) { // check if file is open
|
||||
::close(this->m_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxI2cDriver::open(const char* device) {
|
||||
FW_ASSERT(device);
|
||||
this->m_fd = ::open(device, O_RDWR);
|
||||
return (-1 != this->m_fd);
|
||||
}
|
||||
bool LinuxI2cDriver::open(const char* device) {
|
||||
FW_ASSERT(device);
|
||||
this->m_fd = ::open(device, O_RDWR);
|
||||
return (-1 != this->m_fd);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
// Note this port handler is guarded, so we can make the ioctl call
|
||||
|
||||
// Note this port handler is guarded, so we can make the ioctl call
|
||||
Drv::I2cStatus LinuxI2cDriver ::write_handler(const FwIndexType portNum, U32 addr, Fw::Buffer& serBuffer) {
|
||||
// Make sure file has been opened
|
||||
if (-1 == this->m_fd) {
|
||||
return I2cStatus::I2C_OPEN_ERR;
|
||||
}
|
||||
|
||||
Drv::I2cStatus LinuxI2cDriver ::
|
||||
write_handler(
|
||||
const FwIndexType portNum,
|
||||
U32 addr,
|
||||
Fw::Buffer &serBuffer
|
||||
)
|
||||
{
|
||||
// Make sure file has been opened
|
||||
if (-1 == this->m_fd) {
|
||||
return I2cStatus::I2C_OPEN_ERR;
|
||||
}
|
||||
// select slave address
|
||||
int stat = ioctl(this->m_fd, I2C_SLAVE, addr);
|
||||
if (stat == -1) {
|
||||
return I2cStatus::I2C_ADDRESS_ERR;
|
||||
}
|
||||
// make sure it isn't a null pointer
|
||||
FW_ASSERT(serBuffer.getData());
|
||||
FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
|
||||
// write data
|
||||
ssize_t status_write = write(this->m_fd, serBuffer.getData(), static_cast<size_t>(serBuffer.getSize()));
|
||||
if (status_write == -1) {
|
||||
return I2cStatus::I2C_WRITE_ERR;
|
||||
}
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
|
||||
// select slave address
|
||||
int stat = ioctl(this->m_fd, I2C_SLAVE, addr);
|
||||
if (stat == -1) {
|
||||
return I2cStatus::I2C_ADDRESS_ERR;
|
||||
}
|
||||
// make sure it isn't a null pointer
|
||||
FW_ASSERT(serBuffer.getData());
|
||||
FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
|
||||
// write data
|
||||
ssize_t status_write = write(this->m_fd, serBuffer.getData(), static_cast<size_t>(serBuffer.getSize()));
|
||||
if (status_write == -1) {
|
||||
return I2cStatus::I2C_WRITE_ERR;
|
||||
}
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
Drv::I2cStatus LinuxI2cDriver ::read_handler(const FwIndexType portNum, U32 addr, Fw::Buffer& serBuffer) {
|
||||
// Make sure file has been opened
|
||||
if (-1 == this->m_fd) {
|
||||
return I2cStatus::I2C_OPEN_ERR;
|
||||
}
|
||||
|
||||
Drv::I2cStatus LinuxI2cDriver ::
|
||||
read_handler(
|
||||
const FwIndexType portNum,
|
||||
U32 addr,
|
||||
Fw::Buffer &serBuffer
|
||||
)
|
||||
{
|
||||
// Make sure file has been opened
|
||||
if (-1 == this->m_fd) {
|
||||
return I2cStatus::I2C_OPEN_ERR;
|
||||
}
|
||||
|
||||
// select slave address
|
||||
int stat = ioctl(this->m_fd, I2C_SLAVE, addr);
|
||||
if (stat == -1) {
|
||||
return I2cStatus::I2C_ADDRESS_ERR;
|
||||
}
|
||||
// make sure it isn't a null pointer
|
||||
FW_ASSERT(serBuffer.getData());
|
||||
// read data
|
||||
FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
|
||||
ssize_t status_read = read(this->m_fd, serBuffer.getData(), static_cast<size_t>(serBuffer.getSize()));
|
||||
if (status_read == -1) {
|
||||
return I2cStatus::I2C_READ_ERR;
|
||||
}
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
|
||||
Drv::I2cStatus LinuxI2cDriver ::
|
||||
writeRead_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer &writeBuffer,
|
||||
Fw::Buffer &readBuffer
|
||||
){
|
||||
// select slave address
|
||||
int stat = ioctl(this->m_fd, I2C_SLAVE, addr);
|
||||
if (stat == -1) {
|
||||
return I2cStatus::I2C_ADDRESS_ERR;
|
||||
}
|
||||
// make sure it isn't a null pointer
|
||||
FW_ASSERT(serBuffer.getData());
|
||||
// read data
|
||||
FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
|
||||
ssize_t status_read = read(this->m_fd, serBuffer.getData(), static_cast<size_t>(serBuffer.getSize()));
|
||||
if (status_read == -1) {
|
||||
return I2cStatus::I2C_READ_ERR;
|
||||
}
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
|
||||
Drv::I2cStatus LinuxI2cDriver ::writeRead_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer& writeBuffer,
|
||||
Fw::Buffer& readBuffer) {
|
||||
// Make sure file has been opened
|
||||
if (-1 == this->m_fd) {
|
||||
return I2cStatus::I2C_OPEN_ERR;
|
||||
@ -141,13 +114,13 @@ namespace Drv {
|
||||
|
||||
// Start address
|
||||
rdwr_msgs[0].addr = static_cast<U16>(addr);
|
||||
rdwr_msgs[0].flags = 0; // write
|
||||
rdwr_msgs[0].flags = 0; // write
|
||||
rdwr_msgs[0].len = static_cast<U16>(writeBuffer.getSize());
|
||||
rdwr_msgs[0].buf = writeBuffer.getData();
|
||||
|
||||
// Read buffer
|
||||
rdwr_msgs[1].addr = static_cast<U16>(addr);
|
||||
rdwr_msgs[1].flags = I2C_M_RD; // read
|
||||
rdwr_msgs[1].flags = I2C_M_RD; // read
|
||||
rdwr_msgs[1].len = static_cast<U16>(readBuffer.getSize());
|
||||
rdwr_msgs[1].buf = readBuffer.getData();
|
||||
|
||||
@ -155,15 +128,15 @@ namespace Drv {
|
||||
rdwr_data.msgs = rdwr_msgs;
|
||||
rdwr_data.nmsgs = 2;
|
||||
|
||||
//Use ioctl to perform the combined write/read transaction
|
||||
// Use ioctl to perform the combined write/read transaction
|
||||
int stat = ioctl(this->m_fd, I2C_RDWR, &rdwr_data);
|
||||
|
||||
if(stat == -1){
|
||||
//Because we're using ioctl to perform the transaction we dont know exactly the type of error that occurred
|
||||
return I2cStatus::I2C_OTHER_ERR;
|
||||
if (stat == -1) {
|
||||
// Because we're using ioctl to perform the transaction we dont know exactly the type of error that occurred
|
||||
return I2cStatus::I2C_OTHER_ERR;
|
||||
}
|
||||
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
@ -17,62 +17,51 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class LinuxI2cDriver final :
|
||||
public LinuxI2cDriverComponentBase
|
||||
{
|
||||
class LinuxI2cDriver final : public LinuxI2cDriverComponentBase {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
//! Construct object LinuxI2cDriver
|
||||
//!
|
||||
LinuxI2cDriver(const char* const compName);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
bool open(const char* device);
|
||||
//! Destroy object LinuxI2cDriver
|
||||
//!
|
||||
~LinuxI2cDriver();
|
||||
|
||||
//! Construct object LinuxI2cDriver
|
||||
//!
|
||||
LinuxI2cDriver(const char *const compName);
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool open(const char* device);
|
||||
//! Destroy object LinuxI2cDriver
|
||||
//!
|
||||
~LinuxI2cDriver();
|
||||
//! Handler implementation for write
|
||||
//!
|
||||
I2cStatus write_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer& serBuffer);
|
||||
|
||||
private:
|
||||
//! Handler implementation for read
|
||||
//!
|
||||
I2cStatus read_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer& serBuffer);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
//! Handler implementation for writeRead
|
||||
//!
|
||||
I2cStatus writeRead_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer& writeBuffer,
|
||||
Fw::Buffer& readBuffer);
|
||||
|
||||
//! Handler implementation for write
|
||||
//!
|
||||
I2cStatus write_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer &serBuffer
|
||||
);
|
||||
// Prevent unused field error when using stub
|
||||
#ifndef STUBBED_LINUX_I2C_DRIVER
|
||||
int m_fd; //!< i2c file descriptor
|
||||
#endif
|
||||
};
|
||||
|
||||
//! Handler implementation for read
|
||||
//!
|
||||
I2cStatus read_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer &serBuffer
|
||||
);
|
||||
|
||||
//! Handler implementation for writeRead
|
||||
//!
|
||||
I2cStatus writeRead_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer &writeBuffer,
|
||||
Fw::Buffer &readBuffer
|
||||
);
|
||||
|
||||
// Prevent unused field error when using stub
|
||||
#ifndef STUBBED_LINUX_I2C_DRIVER
|
||||
int m_fd; //!< i2c file descriptor
|
||||
#endif
|
||||
};
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
#endif
|
||||
|
||||
@ -10,68 +10,43 @@
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Drv/LinuxI2cDriver/LinuxI2cDriver.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
LinuxI2cDriver ::LinuxI2cDriver(
|
||||
const char *const compName
|
||||
) : LinuxI2cDriverComponentBase(compName)
|
||||
{
|
||||
LinuxI2cDriver ::LinuxI2cDriver(const char* const compName) : LinuxI2cDriverComponentBase(compName) {}
|
||||
|
||||
}
|
||||
LinuxI2cDriver ::~LinuxI2cDriver() {}
|
||||
|
||||
LinuxI2cDriver ::
|
||||
~LinuxI2cDriver()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool LinuxI2cDriver::open(const char* device) {
|
||||
bool LinuxI2cDriver::open(const char* device) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
// Note this port handler is guarded, so we can make the ioctl call
|
||||
|
||||
// Note this port handler is guarded, so we can make the ioctl call
|
||||
|
||||
I2cStatus LinuxI2cDriver ::
|
||||
write_handler(
|
||||
const FwIndexType portNum,
|
||||
U32 addr,
|
||||
Fw::Buffer &serBuffer
|
||||
)
|
||||
{
|
||||
I2cStatus LinuxI2cDriver ::write_handler(const FwIndexType portNum, U32 addr, Fw::Buffer& serBuffer) {
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
Drv::I2cStatus LinuxI2cDriver ::
|
||||
read_handler(
|
||||
const FwIndexType portNum,
|
||||
U32 addr,
|
||||
Fw::Buffer &serBuffer
|
||||
)
|
||||
{
|
||||
Drv::I2cStatus LinuxI2cDriver ::read_handler(const FwIndexType portNum, U32 addr, Fw::Buffer& serBuffer) {
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
Drv::I2cStatus LinuxI2cDriver ::
|
||||
writeRead_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer &writeBuffer,
|
||||
Fw::Buffer &readBuffer
|
||||
){
|
||||
Drv::I2cStatus LinuxI2cDriver ::writeRead_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
U32 addr,
|
||||
Fw::Buffer& writeBuffer,
|
||||
Fw::Buffer& readBuffer) {
|
||||
return I2cStatus::I2C_OK;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
@ -17,69 +17,45 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
LinuxI2cDriverTester ::
|
||||
LinuxI2cDriverTester() :
|
||||
LinuxI2cDriverGTestBase("Tester", MAX_HISTORY_SIZE),
|
||||
component("LinuxI2cDriver")
|
||||
{
|
||||
LinuxI2cDriverTester ::LinuxI2cDriverTester()
|
||||
: LinuxI2cDriverGTestBase("Tester", MAX_HISTORY_SIZE), component("LinuxI2cDriver") {
|
||||
this->initComponents();
|
||||
this->connectPorts();
|
||||
}
|
||||
}
|
||||
|
||||
LinuxI2cDriverTester ::
|
||||
~LinuxI2cDriverTester()
|
||||
{
|
||||
LinuxI2cDriverTester ::~LinuxI2cDriverTester() {}
|
||||
|
||||
}
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
void LinuxI2cDriverTester ::sendData(U32 addr, U8* data, Fw::Buffer::SizeType size) {
|
||||
Fw::Buffer dataBuff;
|
||||
dataBuff.setdata(static_cast<PlatformPointerCastType>(data));
|
||||
dataBuff.setsize(size);
|
||||
this->invoke_to_write(0, addr, dataBuff);
|
||||
}
|
||||
|
||||
void LinuxI2cDriverTester ::
|
||||
sendData(U32 addr, U8* data, Fw::Buffer::SizeType size)
|
||||
{
|
||||
Fw::Buffer dataBuff;
|
||||
dataBuff.setdata(static_cast<PlatformPointerCastType>(data));
|
||||
dataBuff.setsize(size);
|
||||
this->invoke_to_write(0,addr,dataBuff);
|
||||
}
|
||||
void LinuxI2cDriverTester::open(const char* device) {
|
||||
this->component.open(device);
|
||||
}
|
||||
|
||||
void LinuxI2cDriverTester::open(const char* device) {
|
||||
this->component.open(device);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxI2cDriverTester ::
|
||||
connectPorts()
|
||||
{
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxI2cDriverTester ::connectPorts() {
|
||||
// write
|
||||
this->connect_to_write(
|
||||
0,
|
||||
this->component.get_write_InputPort(0)
|
||||
);
|
||||
this->connect_to_write(0, this->component.get_write_InputPort(0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void LinuxI2cDriverTester ::
|
||||
initComponents()
|
||||
{
|
||||
void LinuxI2cDriverTester ::initComponents() {
|
||||
this->init();
|
||||
this->component.init(
|
||||
INSTANCE
|
||||
);
|
||||
}
|
||||
this->component.init(INSTANCE);
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
@ -18,62 +18,54 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class LinuxI2cDriverTester :
|
||||
public LinuxI2cDriverGTestBase
|
||||
{
|
||||
class LinuxI2cDriverTester : public LinuxI2cDriverGTestBase {
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
//! Construct object LinuxI2cDriverTester
|
||||
//!
|
||||
LinuxI2cDriverTester();
|
||||
|
||||
public:
|
||||
//! Destroy object LinuxI2cDriverTester
|
||||
//!
|
||||
~LinuxI2cDriverTester();
|
||||
|
||||
//! Construct object LinuxI2cDriverTester
|
||||
//!
|
||||
LinuxI2cDriverTester();
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Destroy object LinuxI2cDriverTester
|
||||
//!
|
||||
~LinuxI2cDriverTester();
|
||||
//! To do
|
||||
//!
|
||||
void sendData(U32 addr, U8* data, Fw::Buffer::SizeType size);
|
||||
|
||||
public:
|
||||
void open(const char* device);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! To do
|
||||
//!
|
||||
void sendData(U32 addr, U8* data, Fw::Buffer::SizeType size);
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
void open(const char* device);
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
private:
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
//! The component under test
|
||||
//!
|
||||
LinuxI2cDriver component;
|
||||
};
|
||||
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
private:
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The component under test
|
||||
//!
|
||||
LinuxI2cDriver component;
|
||||
|
||||
};
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,58 +1,53 @@
|
||||
#include <unistd.h>
|
||||
#include <Fw/Types/StringUtils.hpp>
|
||||
#include <LinuxI2cDriverTester.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
|
||||
TEST(TestNominal,Nominal) {
|
||||
|
||||
TEST(TestNominal, Nominal) {
|
||||
Drv::LinuxI2cDriverTester tester;
|
||||
|
||||
}
|
||||
|
||||
const char* help = "[-h] -d <I2C device> -a <I2C address> <byte 0> <byte1> ... <byteN>";
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
int c;
|
||||
|
||||
U32 addr = 0;
|
||||
char device[80];
|
||||
device[0] = 0;
|
||||
|
||||
while ((c = getopt (argc, argv, "hd:a:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "hd:a:")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
printf("test_ut %s\n",argv[0],help);
|
||||
printf("test_ut %s\n", argv[0], help);
|
||||
return 0;
|
||||
case 'a':
|
||||
addr = strtoul(optarg,0,0);
|
||||
addr = strtoul(optarg, 0, 0);
|
||||
break;
|
||||
case 'd':
|
||||
(void) Fw::StringUtils::string_copy(device, optarg, sizeof(device));
|
||||
(void)Fw::StringUtils::string_copy(device, optarg, sizeof(device));
|
||||
break;
|
||||
default:
|
||||
printf("test_ut %s\n",argv[0],help);
|
||||
printf("test_ut %s\n", argv[0], help);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Address: %d (0x%02X) Device: %s\n",addr,addr,device);
|
||||
printf("Address: %d (0x%02X) Device: %s\n", addr, addr, device);
|
||||
|
||||
U8 data[12];
|
||||
|
||||
for (int i = optind; i < argc; i++) {
|
||||
data[optind-i] = strtoul(argv[i],0,0);
|
||||
printf("Data: %s 0x%02X\n",argv[i],data[optind-i]);
|
||||
data[optind - i] = strtoul(argv[i], 0, 0);
|
||||
printf("Data: %s 0x%02X\n", argv[i], data[optind - i]);
|
||||
}
|
||||
|
||||
Drv::LinuxI2cDriverTester tester;
|
||||
tester.open(device);
|
||||
|
||||
tester.sendData(addr,data,argc-optind);
|
||||
tester.sendData(addr, data, argc - optind);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -29,6 +29,18 @@ event SPI_WriteError(
|
||||
format "Error writing/reading SPI device {}.{}: {}" \
|
||||
throttle 5
|
||||
|
||||
@ SPI configuration mismatch
|
||||
event SPI_ConfigMismatch(
|
||||
device: I32 @< The device
|
||||
select: I32 @< The chip select
|
||||
parameter: string @< The parameter being configured
|
||||
write_value: U32 @< The value written
|
||||
read_value: U32 @< The value read
|
||||
) \
|
||||
severity warning low \
|
||||
id 3 \
|
||||
format "SPI device {}.{} configuration mismatch for {}: wrote {}, read {}"
|
||||
|
||||
@ SPI open notification
|
||||
event SPI_PortOpened(
|
||||
device: I32 @< The device
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
using LinuxSpiDriver = LinuxSpiDriverComponentImpl;
|
||||
using LinuxSpiDriver = LinuxSpiDriverComponentImpl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -10,161 +10,193 @@
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
#include <linux/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <Fw/Types/Assert.hpp>
|
||||
#include <Fw/Types/FileNameString.hpp>
|
||||
#include <Fw/Types/String.hpp>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spi/spidev.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
static_assert(FW_USE_PRINTF_FAMILY_FUNCTIONS_IN_STRING_FORMATTING, "Cannot use SPI driver without full string formatting");
|
||||
static_assert(FW_USE_PRINTF_FAMILY_FUNCTIONS_IN_STRING_FORMATTING,
|
||||
"Cannot use SPI driver without full string formatting");
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxSpiDriverComponentImpl::SpiReadWrite_handler(
|
||||
const FwIndexType portNum, Fw::Buffer &writeBuffer,
|
||||
Fw::Buffer &readBuffer) {
|
||||
// @ DEPRECATED: Use SpiWriteRead port instead (same operation with a return value)
|
||||
void LinuxSpiDriverComponentImpl::SpiReadWrite_handler(const FwIndexType portNum,
|
||||
Fw::Buffer& writeBuffer,
|
||||
Fw::Buffer& readBuffer) {
|
||||
FW_ASSERT(portNum >= 0, static_cast<FwAssertArgType>(portNum));
|
||||
FW_ASSERT(writeBuffer.isValid());
|
||||
FW_ASSERT(readBuffer.isValid());
|
||||
(void)SpiWriteRead_handler(portNum, writeBuffer, readBuffer);
|
||||
}
|
||||
|
||||
if (this->m_fd == -1) {
|
||||
return;
|
||||
}
|
||||
SpiStatus LinuxSpiDriverComponentImpl::SpiWriteRead_handler(const FwIndexType portNum,
|
||||
Fw::Buffer& writeBuffer,
|
||||
Fw::Buffer& readBuffer) {
|
||||
FW_ASSERT(portNum >= 0, static_cast<FwAssertArgType>(portNum));
|
||||
FW_ASSERT(writeBuffer.isValid());
|
||||
FW_ASSERT(readBuffer.isValid());
|
||||
FW_ASSERT(writeBuffer.getSize() == readBuffer.getSize());
|
||||
|
||||
spi_ioc_transfer tr;
|
||||
// Zero for unused fields:
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
tr.tx_buf = reinterpret_cast<__u64>(writeBuffer.getData());
|
||||
tr.rx_buf = reinterpret_cast<__u64>(readBuffer.getData());
|
||||
FW_ASSERT_NO_OVERFLOW(writeBuffer.getSize(), __u32);
|
||||
tr.len = static_cast<__u32>(writeBuffer.getSize());
|
||||
/*
|
||||
.speed_hz = 0,
|
||||
.delay_usecs = 0,
|
||||
.bits_per_word = 0,
|
||||
.cs_change = 0,
|
||||
.tx_nbits = 0, // on more-recent kernel versions;
|
||||
.rx_nbits = 0, // on more-recent kernel versions;
|
||||
.pad = 0
|
||||
*/
|
||||
|
||||
int stat = ioctl(this->m_fd, SPI_IOC_MESSAGE(1), &tr);
|
||||
|
||||
if (stat < 1) {
|
||||
this->log_WARNING_HI_SPI_WriteError(this->m_device,this->m_select,stat);
|
||||
}
|
||||
this->m_bytes += readBuffer.getSize();
|
||||
this->tlmWrite_SPI_Bytes(this->m_bytes);
|
||||
if (this->m_fd == -1) {
|
||||
return SpiStatus::SPI_OPEN_ERR;
|
||||
}
|
||||
|
||||
bool LinuxSpiDriverComponentImpl::open(FwIndexType device,
|
||||
FwIndexType select,
|
||||
SpiFrequency clock,
|
||||
SpiMode spiMode) {
|
||||
FW_ASSERT(device >= 0, static_cast<FwAssertArgType>(device));
|
||||
FW_ASSERT(select >= 0, static_cast<FwAssertArgType>(select));
|
||||
spi_ioc_transfer tr;
|
||||
// Zero for unused fields:
|
||||
memset(&tr, 0, sizeof(tr));
|
||||
tr.tx_buf = reinterpret_cast<__u64>(writeBuffer.getData());
|
||||
tr.rx_buf = reinterpret_cast<__u64>(readBuffer.getData());
|
||||
FW_ASSERT_NO_OVERFLOW(writeBuffer.getSize(), __u32);
|
||||
tr.len = static_cast<__u32>(writeBuffer.getSize());
|
||||
/*
|
||||
.speed_hz = 0,
|
||||
.delay_usecs = 0,
|
||||
.bits_per_word = 0,
|
||||
.cs_change = 0,
|
||||
.tx_nbits = 0, // on more-recent kernel versions;
|
||||
.rx_nbits = 0, // on more-recent kernel versions;
|
||||
.pad = 0
|
||||
*/
|
||||
|
||||
this->m_device = device;
|
||||
this->m_select = select;
|
||||
int fd;
|
||||
int ret;
|
||||
int stat = ioctl(this->m_fd, SPI_IOC_MESSAGE(1), &tr);
|
||||
|
||||
// Open:
|
||||
Fw::FileNameString devString;
|
||||
Fw::FormatStatus formatStatus = devString.format("/dev/spidev%" PRI_FwIndexType ".%" PRI_FwIndexType, device, select);
|
||||
FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS);
|
||||
if (stat < 1) {
|
||||
this->log_WARNING_HI_SPI_WriteError(this->m_device, this->m_select, stat);
|
||||
return SpiStatus::SPI_OTHER_ERR;
|
||||
}
|
||||
this->m_bytes += readBuffer.getSize();
|
||||
this->tlmWrite_SPI_Bytes(this->m_bytes);
|
||||
|
||||
fd = ::open(devString.toChar(), O_RDWR);
|
||||
if (fd == -1) {
|
||||
this->log_WARNING_HI_SPI_OpenError(device,select,fd);
|
||||
return false;
|
||||
}
|
||||
return SpiStatus::SPI_OK;
|
||||
}
|
||||
|
||||
this->m_fd = fd;
|
||||
bool LinuxSpiDriverComponentImpl::open(FwIndexType device, FwIndexType select, SpiFrequency clock, SpiMode spiMode) {
|
||||
FW_ASSERT(device >= 0, static_cast<FwAssertArgType>(device));
|
||||
FW_ASSERT(select >= 0, static_cast<FwAssertArgType>(select));
|
||||
|
||||
// Configure:
|
||||
/*
|
||||
* SPI Mode 0, 1, 2, 3
|
||||
*/
|
||||
this->m_device = device;
|
||||
this->m_select = select;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
U8 mode; // Mode Select (CPOL = 0/1, CPHA = 0/1)
|
||||
switch(spiMode) {
|
||||
case SpiMode::SPI_MODE_CPOL_LOW_CPHA_LOW:
|
||||
mode = SPI_MODE_0;
|
||||
break;
|
||||
case SpiMode::SPI_MODE_CPOL_LOW_CPHA_HIGH:
|
||||
mode = SPI_MODE_1;
|
||||
break;
|
||||
case SpiMode::SPI_MODE_CPOL_HIGH_CPHA_LOW:
|
||||
mode = SPI_MODE_2;
|
||||
break;
|
||||
case SpiMode::SPI_MODE_CPOL_HIGH_CPHA_HIGH:
|
||||
mode = SPI_MODE_3;
|
||||
break;
|
||||
default:
|
||||
//Assert if the device SPI Mode is not in the correct range
|
||||
FW_ASSERT(0, spiMode);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device,select,ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device,select,ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 8 bits per word
|
||||
*/
|
||||
U8 bits = 8;
|
||||
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device,select,ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device,select,ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Max speed in Hz
|
||||
*/
|
||||
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &clock);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device,select,ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &clock);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device,select,ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Open:
|
||||
Fw::FileNameString devString;
|
||||
Fw::FormatStatus formatStatus =
|
||||
devString.format("/dev/spidev%" PRI_FwIndexType ".%" PRI_FwIndexType, device, select);
|
||||
FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS);
|
||||
|
||||
fd = ::open(devString.toChar(), O_RDWR);
|
||||
if (fd == -1) {
|
||||
this->log_WARNING_HI_SPI_OpenError(device, select, fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
LinuxSpiDriverComponentImpl::~LinuxSpiDriverComponentImpl() {
|
||||
(void) close(this->m_fd);
|
||||
this->m_fd = fd;
|
||||
|
||||
// Configure:
|
||||
/*
|
||||
* SPI Mode 0, 1, 2, 3
|
||||
*/
|
||||
|
||||
U8 mode; // Mode Select (CPOL = 0/1, CPHA = 0/1)
|
||||
switch (spiMode) {
|
||||
case SpiMode::SPI_MODE_CPOL_LOW_CPHA_LOW:
|
||||
mode = SPI_MODE_0;
|
||||
break;
|
||||
case SpiMode::SPI_MODE_CPOL_LOW_CPHA_HIGH:
|
||||
mode = SPI_MODE_1;
|
||||
break;
|
||||
case SpiMode::SPI_MODE_CPOL_HIGH_CPHA_LOW:
|
||||
mode = SPI_MODE_2;
|
||||
break;
|
||||
case SpiMode::SPI_MODE_CPOL_HIGH_CPHA_HIGH:
|
||||
mode = SPI_MODE_3;
|
||||
break;
|
||||
default:
|
||||
// Assert if the device SPI Mode is not in the correct range
|
||||
FW_ASSERT(0, spiMode);
|
||||
break;
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
U8 read_mode = 0;
|
||||
ret = ioctl(fd, SPI_IOC_RD_MODE, &read_mode);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode != read_mode) {
|
||||
this->log_WARNING_LO_SPI_ConfigMismatch(device, select, Fw::String("MODE"), mode, read_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* 8 bits per word
|
||||
*/
|
||||
U8 bits = 8;
|
||||
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
U8 read_bits = 0;
|
||||
ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &read_bits);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bits != read_bits) {
|
||||
this->log_WARNING_LO_SPI_ConfigMismatch(device, select, Fw::String("BITS_PER_WORD"), bits, read_bits);
|
||||
}
|
||||
|
||||
/*
|
||||
* Max speed in Hz
|
||||
*/
|
||||
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &clock);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
SpiFrequency read_clock;
|
||||
ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &read_clock);
|
||||
if (ret == -1) {
|
||||
this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clock != read_clock) {
|
||||
this->log_WARNING_LO_SPI_ConfigMismatch(device, select, Fw::String("MAX_SPEED_HZ"), clock, read_clock);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LinuxSpiDriverComponentImpl::~LinuxSpiDriverComponentImpl() {
|
||||
(void)close(this->m_fd);
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
|
||||
@ -17,83 +17,84 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
/**
|
||||
* This was taken from the dspal_tester example
|
||||
*
|
||||
* Supported SPI frequency to talk to MPU9x50 slave device
|
||||
* MPU9x50 SPI interface supports upto 20MHz frequency. However 20MHz is not
|
||||
* reliable in our test and corrupted data is observed.
|
||||
*/
|
||||
enum SpiFrequency
|
||||
{
|
||||
SPI_FREQUENCY_1MHZ = 1000000UL,
|
||||
SPI_FREQUENCY_5MHZ = 5000000UL,
|
||||
SPI_FREQUENCY_10MHZ = 10000000UL,
|
||||
SPI_FREQUENCY_15MHZ = 15000000UL,
|
||||
SPI_FREQUENCY_20MHZ = 20000000UL,
|
||||
};
|
||||
/**
|
||||
* This was taken from the dspal_tester example
|
||||
*
|
||||
* Supported SPI frequency to talk to MPU9x50 slave device
|
||||
* MPU9x50 SPI interface supports upto 20MHz frequency. However 20MHz is not
|
||||
* reliable in our test and corrupted data is observed.
|
||||
*/
|
||||
enum SpiFrequency {
|
||||
SPI_FREQUENCY_1MHZ = 1000000UL,
|
||||
SPI_FREQUENCY_5MHZ = 5000000UL,
|
||||
SPI_FREQUENCY_10MHZ = 10000000UL,
|
||||
SPI_FREQUENCY_15MHZ = 15000000UL,
|
||||
SPI_FREQUENCY_20MHZ = 20000000UL,
|
||||
};
|
||||
|
||||
/**
|
||||
* SPI Mode Select
|
||||
*
|
||||
* Defines the SPI Clock Polarity and Phase for each SPI Transaction.
|
||||
*
|
||||
* SPI Clock Polarity(CPOL): Defines clock polarity as idle low (CPOL = 0) or idle high(CPOL = 1)
|
||||
* SPI Clock Phase(CPHA): Defines if data is shifted out on the rising clock edge and sampled
|
||||
* on the falling clock edge(CPHA = 0) or if data is shifted out on the
|
||||
* falling clock edge and sampled on the rising clock edge(CPHA=1)
|
||||
*
|
||||
*/
|
||||
enum SpiMode
|
||||
{
|
||||
SPI_MODE_CPOL_LOW_CPHA_LOW, ///< (CPOL = 0, CPHA = 0)
|
||||
SPI_MODE_CPOL_LOW_CPHA_HIGH,///< (CPOL = 0, CPHA = 1)
|
||||
SPI_MODE_CPOL_HIGH_CPHA_LOW,///< (CPOL = 1, CPHA = 0)
|
||||
SPI_MODE_CPOL_HIGH_CPHA_HIGH,///< (CPOL = 1, CPHA = 1)
|
||||
};
|
||||
/**
|
||||
* SPI Mode Select
|
||||
*
|
||||
* Defines the SPI Clock Polarity and Phase for each SPI Transaction.
|
||||
*
|
||||
* SPI Clock Polarity(CPOL): Defines clock polarity as idle low (CPOL = 0) or idle high(CPOL = 1)
|
||||
* SPI Clock Phase(CPHA): Defines if data is shifted out on the rising clock edge and sampled
|
||||
* on the falling clock edge(CPHA = 0) or if data is shifted out on the
|
||||
* falling clock edge and sampled on the rising clock edge(CPHA=1)
|
||||
*
|
||||
*/
|
||||
enum SpiMode {
|
||||
SPI_MODE_CPOL_LOW_CPHA_LOW, ///< (CPOL = 0, CPHA = 0)
|
||||
SPI_MODE_CPOL_LOW_CPHA_HIGH, ///< (CPOL = 0, CPHA = 1)
|
||||
SPI_MODE_CPOL_HIGH_CPHA_LOW, ///< (CPOL = 1, CPHA = 0)
|
||||
SPI_MODE_CPOL_HIGH_CPHA_HIGH, ///< (CPOL = 1, CPHA = 1)
|
||||
};
|
||||
|
||||
class LinuxSpiDriverComponentImpl final : public LinuxSpiDriverComponentBase {
|
||||
class LinuxSpiDriverComponentImpl final : public LinuxSpiDriverComponentBase {
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
//! Construct object LinuxSpiDriver
|
||||
//!
|
||||
LinuxSpiDriverComponentImpl(const char* const compName /*!< The component name*/
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
//! Destroy object LinuxSpiDriver
|
||||
//!
|
||||
~LinuxSpiDriverComponentImpl();
|
||||
|
||||
//! Construct object LinuxSpiDriver
|
||||
//!
|
||||
LinuxSpiDriverComponentImpl(
|
||||
const char * const compName /*!< The component name*/
|
||||
);
|
||||
//! Open device
|
||||
bool open(FwIndexType device,
|
||||
FwIndexType select,
|
||||
SpiFrequency clock,
|
||||
SpiMode spiMode = SpiMode::SPI_MODE_CPOL_LOW_CPHA_LOW);
|
||||
|
||||
//! Destroy object LinuxSpiDriver
|
||||
//!
|
||||
~LinuxSpiDriverComponentImpl();
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Open device
|
||||
bool open(FwIndexType device,
|
||||
FwIndexType select,
|
||||
SpiFrequency clock,
|
||||
SpiMode spiMode = SpiMode::SPI_MODE_CPOL_LOW_CPHA_LOW);
|
||||
//! Handler implementation for SpiWriteRead
|
||||
//!
|
||||
SpiStatus SpiWriteRead_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer& WriteBuffer,
|
||||
Fw::Buffer& readBuffer);
|
||||
|
||||
private:
|
||||
// @ DEPRECATED: Use SpiWriteRead port instead (same operation with a return value)
|
||||
//! Handler implementation for SpiReadWrite
|
||||
//!
|
||||
void SpiReadWrite_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer& WriteBuffer,
|
||||
Fw::Buffer& readBuffer);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
int m_fd;
|
||||
FwIndexType m_device;
|
||||
FwIndexType m_select;
|
||||
FwSizeType m_bytes;
|
||||
};
|
||||
|
||||
//! Handler implementation for SpiReadWrite
|
||||
//!
|
||||
void SpiReadWrite_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer &WriteBuffer, Fw::Buffer &readBuffer);
|
||||
|
||||
int m_fd;
|
||||
FwIndexType m_device;
|
||||
FwIndexType m_select;
|
||||
FwSizeType m_bytes;
|
||||
|
||||
};
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
#endif
|
||||
|
||||
@ -15,19 +15,11 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
LinuxSpiDriverComponentImpl::
|
||||
LinuxSpiDriverComponentImpl(const char * const compName) :
|
||||
LinuxSpiDriverComponentBase(compName),
|
||||
m_fd(-1),
|
||||
m_device(-1),
|
||||
m_select(-1),
|
||||
m_bytes(0)
|
||||
{
|
||||
LinuxSpiDriverComponentImpl::LinuxSpiDriverComponentImpl(const char* const compName)
|
||||
: LinuxSpiDriverComponentBase(compName), m_fd(-1), m_device(-1), m_select(-1), m_bytes(0) {}
|
||||
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
@ -15,24 +15,25 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
bool LinuxSpiDriverComponentImpl::open(FwIndexType device,
|
||||
FwIndexType select,
|
||||
SpiFrequency clock,
|
||||
SpiMode spiMode) {
|
||||
return false;
|
||||
}
|
||||
bool LinuxSpiDriverComponentImpl::open(FwIndexType device, FwIndexType select, SpiFrequency clock, SpiMode spiMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxSpiDriverComponentImpl::SpiReadWrite_handler(
|
||||
const FwIndexType portNum, Fw::Buffer &WriteBuffer,
|
||||
Fw::Buffer &readBuffer) {
|
||||
}
|
||||
SpiStatus LinuxSpiDriverComponentImpl::SpiWriteRead_handler(const FwIndexType portNum,
|
||||
Fw::Buffer& WriteBuffer,
|
||||
Fw::Buffer& readBuffer) {
|
||||
return SpiStatus::SPI_OK;
|
||||
}
|
||||
|
||||
LinuxSpiDriverComponentImpl::~LinuxSpiDriverComponentImpl() {
|
||||
// @ DEPRECATED: Use SpiWriteRead port instead (same operation with a return value)
|
||||
void LinuxSpiDriverComponentImpl::SpiReadWrite_handler(const FwIndexType portNum,
|
||||
Fw::Buffer& WriteBuffer,
|
||||
Fw::Buffer& readBuffer) {}
|
||||
|
||||
}
|
||||
LinuxSpiDriverComponentImpl::~LinuxSpiDriverComponentImpl() {}
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
@ -17,114 +17,84 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
LinuxSpiDriverTester ::
|
||||
LinuxSpiDriverTester() :
|
||||
LinuxSpiDriverTesterBase("Tester", MAX_HISTORY_SIZE),
|
||||
component("LinuxSpiDriver")
|
||||
{
|
||||
LinuxSpiDriverTester ::LinuxSpiDriverTester()
|
||||
: LinuxSpiDriverTesterBase("Tester", MAX_HISTORY_SIZE), component("LinuxSpiDriver") {
|
||||
this->initComponents();
|
||||
this->connectPorts();
|
||||
}
|
||||
}
|
||||
|
||||
LinuxSpiDriverTester ::
|
||||
~LinuxSpiDriverTester()
|
||||
{
|
||||
LinuxSpiDriverTester ::~LinuxSpiDriverTester() {}
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxSpiDriverTester ::
|
||||
connectPorts()
|
||||
{
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxSpiDriverTester ::connectPorts() {
|
||||
// SpiReadWrite
|
||||
this->connect_to_SpiReadWrite(
|
||||
0,
|
||||
this->component.get_SpiReadWrite_InputPort(0)
|
||||
);
|
||||
this->connect_to_SpiReadWrite(0, this->component.get_SpiReadWrite_InputPort(0));
|
||||
|
||||
// Tlm
|
||||
this->component.set_Tlm_OutputPort(
|
||||
0,
|
||||
this->get_from_Tlm(0)
|
||||
);
|
||||
this->component.set_Tlm_OutputPort(0, this->get_from_Tlm(0));
|
||||
|
||||
// Time
|
||||
this->component.set_Time_OutputPort(
|
||||
0,
|
||||
this->get_from_Time(0)
|
||||
);
|
||||
this->component.set_Time_OutputPort(0, this->get_from_Time(0));
|
||||
|
||||
// Log
|
||||
this->component.set_Log_OutputPort(
|
||||
0,
|
||||
this->get_from_Log(0)
|
||||
);
|
||||
this->component.set_Log_OutputPort(0, this->get_from_Log(0));
|
||||
|
||||
// LogText
|
||||
this->component.set_LogText_OutputPort(
|
||||
0,
|
||||
this->get_from_LogText(0)
|
||||
);
|
||||
this->component.set_LogText_OutputPort(0, this->get_from_LogText(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LinuxSpiDriverTester ::
|
||||
initComponents()
|
||||
{
|
||||
void LinuxSpiDriverTester ::initComponents() {
|
||||
this->init();
|
||||
this->component.init(
|
||||
INSTANCE
|
||||
);
|
||||
this->component.init(INSTANCE);
|
||||
|
||||
this->component.open(8,0,SPI_FREQUENCY_1MHZ);
|
||||
}
|
||||
this->component.open(8, 0, SPI_FREQUENCY_1MHZ);
|
||||
}
|
||||
|
||||
void LinuxSpiDriverTester::textLogIn(const FwEventIdType id, //!< The event ID
|
||||
Fw::Time& timeTag, //!< The time
|
||||
const Fw::TextLogSeverity severity, //!< The severity
|
||||
const Fw::TextLogString& text //!< The event string
|
||||
) {
|
||||
TextLogEntry e = { id, timeTag, severity, text };
|
||||
void LinuxSpiDriverTester::textLogIn(const FwEventIdType id, //!< The event ID
|
||||
Fw::Time& timeTag, //!< The time
|
||||
const Fw::TextLogSeverity severity, //!< The severity
|
||||
const Fw::TextLogString& text //!< The event string
|
||||
) {
|
||||
TextLogEntry e = {id, timeTag, severity, text};
|
||||
|
||||
printTextLogHistoryEntry(e, stdout);
|
||||
}
|
||||
printTextLogHistoryEntry(e, stdout);
|
||||
}
|
||||
|
||||
void LinuxSpiDriverTester::sendBuffer(BYTE* buffer, FwSizeType size) {
|
||||
Fw::Buffer w;
|
||||
w.setdata(reinterpret_cast<PlatformPointerCastType>(buffer));
|
||||
w.setsize(size);
|
||||
void LinuxSpiDriverTester::sendBuffer(BYTE* buffer, FwSizeType size) {
|
||||
Fw::Buffer w;
|
||||
w.setdata(reinterpret_cast<PlatformPointerCastType>(buffer));
|
||||
w.setsize(size);
|
||||
|
||||
printf("WRITE: ");
|
||||
for (FwSizeType byte = 0; byte < size; byte++) {
|
||||
printf("0x%02X ",buffer[byte]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("WRITE: ");
|
||||
for (FwSizeType byte = 0; byte < size; byte++) {
|
||||
printf("0x%02X ", buffer[byte]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
BYTE* rb = 0;
|
||||
rb = new BYTE[size];
|
||||
BYTE* rb = 0;
|
||||
rb = new BYTE[size];
|
||||
|
||||
FW_ASSERT(rb);
|
||||
FW_ASSERT(rb);
|
||||
|
||||
Fw::Buffer r(0,0, reinterpret_cast<PlatformPointerCastType>(rb),size);
|
||||
Fw::Buffer r(0, 0, reinterpret_cast<PlatformPointerCastType>(rb), size);
|
||||
|
||||
this->invoke_to_SpiReadWrite(0,w,r);
|
||||
this->invoke_to_SpiReadWrite(0, w, r);
|
||||
|
||||
BYTE* d = (BYTE*)r.getdata();
|
||||
printf("READ: ");
|
||||
for (FwSizeType byte = 0; byte < size; byte++) {
|
||||
printf("0x%02X ",d[byte]);
|
||||
}
|
||||
printf("\n");
|
||||
BYTE* d = (BYTE*)r.getdata();
|
||||
printf("READ: ");
|
||||
for (FwSizeType byte = 0; byte < size; byte++) {
|
||||
printf("0x%02X ", d[byte]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
delete[] rb;
|
||||
}
|
||||
delete[] rb;
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
@ -13,72 +13,63 @@
|
||||
#ifndef TESTER_HPP
|
||||
#define TESTER_HPP
|
||||
|
||||
#include "LinuxSpiDriverGTestBase.hpp"
|
||||
#include "Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp"
|
||||
#include "LinuxSpiDriverGTestBase.hpp"
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class LinuxSpiDriverTester :
|
||||
public LinuxSpiDriverTesterBase
|
||||
{
|
||||
class LinuxSpiDriverTester : public LinuxSpiDriverTesterBase {
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
//! Construct object LinuxSpiDriverTester
|
||||
//!
|
||||
LinuxSpiDriverTester();
|
||||
|
||||
public:
|
||||
//! Destroy object LinuxSpiDriverTester
|
||||
//!
|
||||
~LinuxSpiDriverTester();
|
||||
|
||||
//! Construct object LinuxSpiDriverTester
|
||||
//!
|
||||
LinuxSpiDriverTester();
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Destroy object LinuxSpiDriverTester
|
||||
//!
|
||||
~LinuxSpiDriverTester();
|
||||
//! To do
|
||||
//!
|
||||
void sendBuffer(U8* buffer, FwSizeType size);
|
||||
|
||||
public:
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
//! To do
|
||||
//!
|
||||
void sendBuffer(U8* buffer, FwSizeType size);
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
private:
|
||||
void textLogIn(const FwEventIdType id, //!< The event ID
|
||||
Fw::Time& timeTag, //!< The time
|
||||
const Fw::TextLogSeverity severity, //!< The severity
|
||||
const Fw::TextLogString& text //!< The event string
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
//! The component under test
|
||||
//!
|
||||
LinuxSpiDriverComponentImpl component;
|
||||
};
|
||||
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
void textLogIn(const FwEventIdType id, //!< The event ID
|
||||
Fw::Time& timeTag, //!< The time
|
||||
const Fw::TextLogSeverity severity, //!< The severity
|
||||
const Fw::TextLogString& text //!< The event string
|
||||
);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The component under test
|
||||
//!
|
||||
LinuxSpiDriverComponentImpl component;
|
||||
|
||||
};
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
#endif
|
||||
|
||||
@ -2,25 +2,24 @@
|
||||
// Main.cpp
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include "LinuxSpiDriverTester.hpp"
|
||||
#include <cstdlib>
|
||||
#include "LinuxSpiDriverTester.hpp"
|
||||
|
||||
//TEST(Test, NominalTlm) {
|
||||
// Svc::LinuxSpiDriverTester tester;
|
||||
// tester.nominalTlm();
|
||||
//}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// TEST(Test, NominalTlm) {
|
||||
// Svc::LinuxSpiDriverTester tester;
|
||||
// tester.nominalTlm();
|
||||
// }
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Drv::LinuxSpiDriverTester tester;
|
||||
|
||||
U8 buffer[argc-1];
|
||||
U8 buffer[argc - 1];
|
||||
|
||||
// scan args for bytes
|
||||
|
||||
for (int byte = 0; byte < argc-1; byte++) {
|
||||
buffer[byte] = strtol(argv[1+byte],0,0);
|
||||
for (int byte = 0; byte < argc - 1; byte++) {
|
||||
buffer[byte] = strtol(argv[1 + byte], 0, 0);
|
||||
}
|
||||
|
||||
tester.sendBuffer(buffer,sizeof(buffer));
|
||||
tester.sendBuffer(buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <cerrno>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Drv {
|
||||
|
||||
@ -28,8 +28,13 @@ namespace Drv {
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
LinuxUartDriver ::LinuxUartDriver(const char* const compName)
|
||||
: LinuxUartDriverComponentBase(compName), m_fd(-1), m_allocationSize(0), m_device("NOT_EXIST"), m_quitReadThread(false) {
|
||||
}
|
||||
: LinuxUartDriverComponentBase(compName),
|
||||
m_fd(-1),
|
||||
m_allocationSize(0),
|
||||
m_device("NOT_EXIST"),
|
||||
m_bytesSent(0),
|
||||
m_bytesReceived(0),
|
||||
m_quitReadThread(false) {}
|
||||
|
||||
bool LinuxUartDriver::open(const char* const device,
|
||||
UartBaudRate baud,
|
||||
@ -43,7 +48,6 @@ bool LinuxUartDriver::open(const char* const device,
|
||||
|
||||
this->m_device = device;
|
||||
|
||||
|
||||
/*
|
||||
The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port. If you
|
||||
don't specify this then any input (such as keyboard abort signals and so forth) will affect your process. Programs
|
||||
@ -277,7 +281,7 @@ bool LinuxUartDriver::open(const char* const device,
|
||||
Fw::LogStringArg _arg = device;
|
||||
this->log_ACTIVITY_HI_PortOpened(_arg);
|
||||
if (this->isConnected_ready_OutputPort(0)) {
|
||||
this->ready_out(0); // Indicate the driver is connected
|
||||
this->ready_out(0); // Indicate the driver is connected
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -292,28 +296,33 @@ LinuxUartDriver ::~LinuxUartDriver() {
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
|
||||
void LinuxUartDriver ::run_handler(FwIndexType portNum, U32 context) {
|
||||
this->tlmWrite_BytesSent(this->m_bytesSent);
|
||||
this->tlmWrite_BytesRecv(this->m_bytesReceived);
|
||||
}
|
||||
|
||||
Drv::ByteStreamStatus LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
|
||||
Drv::ByteStreamStatus status = Drv::ByteStreamStatus::OP_OK;
|
||||
if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
|
||||
status = Drv::ByteStreamStatus::OTHER_ERROR;
|
||||
} else {
|
||||
unsigned char *data = serBuffer.getData();
|
||||
unsigned char* data = serBuffer.getData();
|
||||
FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
|
||||
size_t xferSize = static_cast<size_t>(serBuffer.getSize());
|
||||
|
||||
ssize_t stat = ::write(this->m_fd, data, xferSize);
|
||||
|
||||
if (-1 == stat || static_cast<size_t>(stat) != xferSize) {
|
||||
Fw::LogStringArg _arg = this->m_device;
|
||||
this->log_WARNING_HI_WriteError(_arg, static_cast<I32>(stat));
|
||||
status = Drv::ByteStreamStatus::OTHER_ERROR;
|
||||
Fw::LogStringArg _arg = this->m_device;
|
||||
this->log_WARNING_HI_WriteError(_arg, static_cast<I32>(stat));
|
||||
status = Drv::ByteStreamStatus::OTHER_ERROR;
|
||||
} else {
|
||||
this->m_bytesSent += static_cast<FwSizeType>(stat);
|
||||
}
|
||||
}
|
||||
// Return the buffer back to the caller
|
||||
sendReturnOut_out(0, serBuffer, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
void LinuxUartDriver::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
this->deallocate_out(0, fwBuffer);
|
||||
}
|
||||
@ -323,7 +332,7 @@ void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
|
||||
Drv::ByteStreamStatus status = ByteStreamStatus::OTHER_ERROR; // added by m.chase 03.06.2017
|
||||
LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
|
||||
while (!comp->m_quitReadThread) {
|
||||
Fw::Buffer buff = comp->allocate_out(0,comp->m_allocationSize);
|
||||
Fw::Buffer buff = comp->allocate_out(0, comp->m_allocationSize);
|
||||
|
||||
// On failed allocation, error
|
||||
if (buff.getData() == nullptr) {
|
||||
@ -356,14 +365,18 @@ void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
|
||||
} else if (stat > 0) {
|
||||
buff.setSize(static_cast<U32>(stat));
|
||||
status = ByteStreamStatus::OP_OK; // added by m.chase 03.06.2017
|
||||
comp->m_bytesReceived += static_cast<FwSizeType>(stat);
|
||||
} else {
|
||||
status = ByteStreamStatus::OTHER_ERROR; // Simply to return the buffer
|
||||
status = ByteStreamStatus::OTHER_ERROR; // Simply to return the buffer
|
||||
}
|
||||
|
||||
comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxUartDriver ::start(FwTaskPriorityType priority, Os::Task::ParamType stackSize, Os::Task::ParamType cpuAffinity) {
|
||||
void LinuxUartDriver ::start(FwTaskPriorityType priority,
|
||||
Os::Task::ParamType stackSize,
|
||||
Os::Task::ParamType cpuAffinity) {
|
||||
Os::TaskString task("SerReader");
|
||||
Os::Task::Arguments arguments(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
|
||||
Os::Task::Status stat = this->m_readTask.start(arguments);
|
||||
|
||||
@ -14,6 +14,9 @@ module Drv {
|
||||
@ Deallocation of allocated buffers
|
||||
output port deallocate: Fw.BufferSend
|
||||
|
||||
@ The rate group input for sending telemetry
|
||||
sync input port run: Svc.Sched
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Special ports
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <Os/Task.hpp>
|
||||
|
||||
#include <termios.h>
|
||||
#include <atomic>
|
||||
|
||||
namespace Drv {
|
||||
|
||||
@ -34,30 +35,30 @@ class LinuxUartDriver final : public LinuxUartDriverComponentBase {
|
||||
|
||||
//! Configure UART parameters
|
||||
enum UartBaudRate {
|
||||
BAUD_9600=9600,
|
||||
BAUD_19200=19200,
|
||||
BAUD_38400=38400,
|
||||
BAUD_57600=57600,
|
||||
BAUD_115K=115200,
|
||||
BAUD_230K=230400,
|
||||
BAUD_9600 = 9600,
|
||||
BAUD_19200 = 19200,
|
||||
BAUD_38400 = 38400,
|
||||
BAUD_57600 = 57600,
|
||||
BAUD_115K = 115200,
|
||||
BAUD_230K = 230400,
|
||||
#ifdef TGT_OS_TYPE_LINUX
|
||||
BAUD_460K=460800,
|
||||
BAUD_921K=921600,
|
||||
BAUD_1000K=1000000,
|
||||
BAUD_1152K=1152000,
|
||||
BAUD_1500K=1500000,
|
||||
BAUD_2000K=2000000,
|
||||
BAUD_460K = 460800,
|
||||
BAUD_921K = 921600,
|
||||
BAUD_1000K = 1000000,
|
||||
BAUD_1152K = 1152000,
|
||||
BAUD_1500K = 1500000,
|
||||
BAUD_2000K = 2000000,
|
||||
#ifdef B2500000
|
||||
BAUD_2500K=2500000,
|
||||
BAUD_2500K = 2500000,
|
||||
#endif
|
||||
#ifdef B3000000
|
||||
BAUD_3000K=3000000,
|
||||
BAUD_3000K = 3000000,
|
||||
#endif
|
||||
#ifdef B3500000
|
||||
BAUD_3500K=3500000,
|
||||
BAUD_3500K = 3500000,
|
||||
#endif
|
||||
#ifdef B4000000
|
||||
BAUD_4000K=4000000
|
||||
BAUD_4000K = 4000000
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
@ -67,7 +68,11 @@ class LinuxUartDriver final : public LinuxUartDriverComponentBase {
|
||||
enum UartParity { PARITY_NONE, PARITY_ODD, PARITY_EVEN };
|
||||
|
||||
// Open device with specified baud and flow control.
|
||||
bool open(const char* const device, UartBaudRate baud, UartFlowControl fc, UartParity parity, FwSizeType allocationSize);
|
||||
bool open(const char* const device,
|
||||
UartBaudRate baud,
|
||||
UartFlowControl fc,
|
||||
UartParity parity,
|
||||
FwSizeType allocationSize);
|
||||
|
||||
//! start the serial poll thread.
|
||||
//! buffSize is the max receive buffer size
|
||||
@ -91,29 +96,37 @@ class LinuxUartDriver final : public LinuxUartDriverComponentBase {
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Handler implementation for run
|
||||
//!
|
||||
//! The rate group input for sending telemetry
|
||||
void run_handler(FwIndexType portNum, //!< The port number
|
||||
U32 context //!< The call order
|
||||
) override;
|
||||
|
||||
//! Handler implementation for serialSend
|
||||
//!
|
||||
void send_handler(FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer& serBuffer) override;
|
||||
Drv::ByteStreamStatus send_handler(FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer& serBuffer) override;
|
||||
|
||||
//! Handler implementation for recvReturnIn
|
||||
//!
|
||||
//! Port receiving back ownership of data sent out on $recv port
|
||||
void recvReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
|
||||
int m_fd; //!< file descriptor returned for I/O device
|
||||
FwSizeType m_allocationSize; //!< size of allocation request to memory manager
|
||||
const char* m_device; //!< original device path
|
||||
int m_fd; //!< file descriptor returned for I/O device
|
||||
FwSizeType m_allocationSize; //!< size of allocation request to memory manager
|
||||
const char* m_device; //!< original device path
|
||||
|
||||
//! This method will be called by the new thread to wait for input on the serial port.
|
||||
static void serialReadTaskEntry(void* ptr);
|
||||
|
||||
Os::Task m_readTask; //!< task instance for thread to read serial port
|
||||
|
||||
|
||||
bool m_quitReadThread; //!< flag to quit thread
|
||||
std::atomic<FwSizeType> m_bytesSent; //!< number of bytes sent
|
||||
std::atomic<FwSizeType> m_bytesReceived; //!< number of bytes received
|
||||
bool m_quitReadThread; //!< flag to quit thread
|
||||
};
|
||||
|
||||
} // end namespace Drv
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@ Bytes Sent
|
||||
telemetry BytesSent: U32 id 0
|
||||
telemetry BytesSent: FwSizeType id 0
|
||||
|
||||
@ Bytes Received
|
||||
telemetry BytesRecv: U32 id 1
|
||||
telemetry BytesRecv: FwSizeType id 1
|
||||
|
||||
177
Drv/LinuxUartDriver/docs/sdd.md
Normal file
177
Drv/LinuxUartDriver/docs/sdd.md
Normal file
@ -0,0 +1,177 @@
|
||||
# Drv::LinuxUartDriver
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
The LinuxUartDriver component provides a Linux-specific implementation of a UART (Universal Asynchronous Receiver-Transmitter) serial communication driver. It implements the byte stream driver model interface (see [`Drv.ByteStreamDriver`](../../Interfaces/ByteStreamDriver.fpp)) to enable serial communication with external devices through UART ports on Linux systems.
|
||||
|
||||
The component wraps Linux termios API functionality to provide configurable serial communication with support for various baud rates, flow control options, and parity settings. It implements bidirectional communication using a dedicated receive thread and synchronous send operations.
|
||||
|
||||
For more information on the ByteStreamDriverModel see: [`Drv::ByteStreamDriverModel`](../../ByteStreamDriverModel/docs/sdd.md).
|
||||
|
||||
## 2. Requirements
|
||||
|
||||
| Name | Description | Validation |
|
||||
|---|---|---|
|
||||
| LINUX-UART-COMP-001 | The LinuxUartDriver component shall implement the Drv.ByteStreamDriver interface | inspection |
|
||||
| LINUX-UART-COMP-002 | The LinuxUartDriver component shall provide configurable baud rates from 9600 to 4MHz | inspection |
|
||||
| LINUX-UART-COMP-003 | The LinuxUartDriver component shall provide configurable flow control (none/hardware) | inspection |
|
||||
| LINUX-UART-COMP-004 | The LinuxUartDriver component shall provide configurable parity (none/odd/even) | inspection |
|
||||
| LINUX-UART-COMP-005 | The LinuxUartDriver component shall provide a dedicated read thread for receiving data | inspection |
|
||||
| LINUX-UART-COMP-006 | The LinuxUartDriver component shall report telemetry for bytes sent and received | inspection |
|
||||
| LINUX-UART-COMP-007 | The LinuxUartDriver component shall handle UART errors and report them via events | inspection |
|
||||
| LINUX-UART-COMP-008 | The LinuxUartDriver component shall support buffer allocation for receive operations | inspection |
|
||||
|
||||
## 3. Design
|
||||
|
||||
The LinuxUartDriver component implements the design specified by the [`Drv.ByteStreamDriver`](../../Interfaces/ByteStreamDriver.fpp) interface.
|
||||
|
||||
### 3.1 Architecture
|
||||
|
||||
The component consists of the following key elements:
|
||||
|
||||
- **UART Configuration**: Handles device opening, baud rate, flow control, and parity settings using Linux termios API
|
||||
- **Send Handler**: Synchronous transmission of data via the `send` port (guarded input port)
|
||||
- **Receive Thread**: Asynchronous reception of data via a dedicated thread that calls the `recv` output port
|
||||
- **Buffer Management**: Integration with F´ buffer allocation system for memory management
|
||||
- **Telemetry Reporting**: Tracks and reports bytes sent and received statistics
|
||||
- **Error Handling**: Comprehensive error detection and event reporting
|
||||
|
||||
### 3.2 Send Operation
|
||||
|
||||
When data is sent via the `send` input port:
|
||||
1. The component validates the file descriptor and buffer
|
||||
2. Data is written to the UART device using the Linux `write()` system call
|
||||
3. Bytes sent counter is updated for telemetry
|
||||
4. Status is returned indicating success or failure
|
||||
|
||||
### 3.3 Receive Operation
|
||||
|
||||
The receive operation runs in a separate thread:
|
||||
1. A buffer is allocated from the buffer manager
|
||||
2. The thread blocks on `read()` waiting for incoming data
|
||||
3. Received data is packaged in the buffer and sent via `recv` output port
|
||||
4. Bytes received counter is updated for telemetry
|
||||
5. Errors are logged and reported via events
|
||||
|
||||
### 3.4 Threading Model
|
||||
|
||||
The component uses a single dedicated thread for receive operations (`serialReadTaskEntry`). This thread:
|
||||
- Runs continuously until `quitReadThread()` is called
|
||||
- Allocates buffers for each receive operation
|
||||
- Handles timeouts and errors gracefully
|
||||
- Can be started with configurable priority and stack size
|
||||
|
||||
## 4. Usage
|
||||
|
||||
The LinuxUartDriver must be configured with device parameters before use. The typical usage pattern is:
|
||||
|
||||
1. **Open Device**: Configure the UART device with desired parameters
|
||||
2. **Start Receive Thread**: Begin the receive thread for incoming data
|
||||
3. **Send/Receive Data**: Use the ByteStreamDriverModel ports for communication
|
||||
4. **Shutdown**: Stop the receive thread and close the device
|
||||
|
||||
### 4.1 Configuration Example
|
||||
|
||||
The LinuxUartDriver should be instantiated in the FPP topology and configured using separate functions following F´ patterns:
|
||||
|
||||
```cpp
|
||||
// Configuration function - called during topology setup
|
||||
void configureTopology() {
|
||||
// Open UART device with configuration
|
||||
bool success = uart.open("/dev/ttyUSB0", // Device path
|
||||
Drv::LinuxUartDriver::BAUD_115K, // 115200 baud rate
|
||||
Drv::LinuxUartDriver::NO_FLOW, // No flow control
|
||||
Drv::LinuxUartDriver::PARITY_NONE, // No parity
|
||||
1024); // Buffer size
|
||||
if (!success) {
|
||||
// Handle configuration error
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
// Startup function - called when starting tasks
|
||||
void setupTopology() {
|
||||
// Start receive thread
|
||||
uart.start(UART_PRIORITY, // Thread priority
|
||||
32 * 1024, // Thread stack size
|
||||
Os::Task::TASK_DEFAULT); // Thread CPU affinity mask
|
||||
}
|
||||
|
||||
// Shutdown function - called during teardown
|
||||
void teardownTopology() {
|
||||
uart.quitReadThread();
|
||||
uart.join();
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 Integration with Rate Groups
|
||||
|
||||
The component includes a `run` input port for telemetry reporting that should be connected to a rate group in the FPP topology:
|
||||
|
||||
```fpp
|
||||
# In topology.fpp connections section
|
||||
connections RateGroups {
|
||||
# Connect UART driver to rate group for telemetry
|
||||
rateGroup1Comp.RateGroupMemberOut[N] -> uart.run
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Configuration
|
||||
|
||||
### 5.1 Device Parameters
|
||||
|
||||
| Parameter | Type | Description | Valid Values |
|
||||
|-----------|------|-------------|--------------|
|
||||
| device | const char* | Path to UART device | Linux device path (e.g., "/dev/ttyUSB0") |
|
||||
| baud | Drv::LinuxUartDriver::UartBaudRate | Communication baud rate | See baud rate enumeration |
|
||||
| fc | Drv::LinuxUartDriver::UartFlowControl | Flow control setting | NO_FLOW, HW_FLOW |
|
||||
| parity | Drv::LinuxUartDriver::UartParity | Parity setting | PARITY_NONE, PARITY_ODD, PARITY_EVEN |
|
||||
| allocationSize | FwSizeType | Receive buffer size | Positive integer (bytes) |
|
||||
|
||||
### 5.2 Baud Rate Options
|
||||
|
||||
The component supports the following baud rates:
|
||||
|
||||
| Enumeration | Numeric Value | Availability |
|
||||
|-------------|---------------|--------------|
|
||||
| BAUD_9600 | 9600 | All platforms |
|
||||
| BAUD_19200 | 19200 | All platforms |
|
||||
| BAUD_38400 | 38400 | All platforms |
|
||||
| BAUD_57600 | 57600 | All platforms |
|
||||
| BAUD_115K | 115200 | All platforms |
|
||||
| BAUD_230K | 230400 | All platforms |
|
||||
| BAUD_460K | 460800 | Linux only |
|
||||
| BAUD_921K | 921600 | Linux only |
|
||||
| BAUD_1000K | 1000000 | Linux only |
|
||||
| BAUD_1152K | 1152000 | Linux only |
|
||||
| BAUD_1500K | 1500000 | Linux only |
|
||||
| BAUD_2000K | 2000000 | Linux only |
|
||||
| BAUD_2500K | 2500000 | Linux only (if supported) |
|
||||
| BAUD_3000K | 3000000 | Linux only (if supported) |
|
||||
| BAUD_3500K | 3500000 | Linux only (if supported) |
|
||||
| BAUD_4000K | 4000000 | Linux only (if supported) |
|
||||
|
||||
### 5.3 Thread Configuration
|
||||
|
||||
The receive thread can be configured with:
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| priority | FwTaskPriorityType | TASK_PRIORITY_DEFAULT | Thread priority |
|
||||
| stackSize | Os::Task::ParamType | TASK_DEFAULT | Thread stack size |
|
||||
| cpuAffinity | Os::Task::ParamType | TASK_DEFAULT | CPU affinity mask |
|
||||
|
||||
### 5.4 Events and Telemetry
|
||||
|
||||
The component generates the following events:
|
||||
- **OpenError**: UART device open failures
|
||||
- **ConfigError**: UART configuration failures
|
||||
- **WriteError**: Data transmission errors
|
||||
- **ReadError**: Data reception errors
|
||||
- **PortOpened**: Successful device configuration
|
||||
- **NoBuffers**: Buffer allocation failures
|
||||
- **BufferTooSmall**: Insufficient buffer size
|
||||
|
||||
The component reports the following telemetry:
|
||||
- **BytesSent**: Total bytes transmitted
|
||||
- **BytesRecv**: Total bytes received
|
||||
@ -3,42 +3,44 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
DataBuffer::DataBuffer(const U8 *args, FwSizeType size) {
|
||||
Fw::SerializeStatus stat = Fw::SerializeBufferBase::setBuff(args,size);
|
||||
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat,static_cast<FwAssertArgType>(stat));
|
||||
}
|
||||
DataBuffer::DataBuffer(const U8* args, FwSizeType size) {
|
||||
Fw::SerializeStatus stat = Fw::SerializeBufferBase::setBuff(args, size);
|
||||
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, static_cast<FwAssertArgType>(stat));
|
||||
}
|
||||
|
||||
DataBuffer::DataBuffer() {
|
||||
}
|
||||
DataBuffer::DataBuffer() {}
|
||||
|
||||
DataBuffer::~DataBuffer() {
|
||||
}
|
||||
DataBuffer::~DataBuffer() {}
|
||||
|
||||
DataBuffer::DataBuffer(const DataBuffer& other) : Fw::SerializeBufferBase() {
|
||||
Fw::SerializeStatus stat = Fw::SerializeBufferBase::setBuff(other.m_data,other.getBuffLength());
|
||||
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat,static_cast<FwAssertArgType>(stat));
|
||||
}
|
||||
DataBuffer::DataBuffer(const DataBuffer& other) : Fw::SerializeBufferBase() {
|
||||
Fw::SerializeStatus stat = Fw::SerializeBufferBase::setBuff(other.m_data, other.getSize());
|
||||
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, static_cast<FwAssertArgType>(stat));
|
||||
}
|
||||
|
||||
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||
if(this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fw::SerializeStatus stat = Fw::SerializeBufferBase::setBuff(other.m_data,other.getBuffLength());
|
||||
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat,static_cast<FwAssertArgType>(stat));
|
||||
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
FwSizeType DataBuffer::getBuffCapacity() const {
|
||||
return sizeof(this->m_data);
|
||||
}
|
||||
|
||||
const U8* DataBuffer::getBuffAddr() const {
|
||||
return this->m_data;
|
||||
}
|
||||
|
||||
U8* DataBuffer::getBuffAddr() {
|
||||
return this->m_data;
|
||||
}
|
||||
|
||||
Fw::SerializeStatus stat = Fw::SerializeBufferBase::setBuff(other.m_data, other.getSize());
|
||||
FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, static_cast<FwAssertArgType>(stat));
|
||||
return *this;
|
||||
}
|
||||
|
||||
FwSizeType DataBuffer::getCapacity() const {
|
||||
return sizeof(this->m_data);
|
||||
}
|
||||
|
||||
FwSizeType DataBuffer::getBuffCapacity() const {
|
||||
return this->getCapacity();
|
||||
}
|
||||
|
||||
const U8* DataBuffer::getBuffAddr() const {
|
||||
return this->m_data;
|
||||
}
|
||||
|
||||
U8* DataBuffer::getBuffAddr() {
|
||||
return this->m_data;
|
||||
}
|
||||
|
||||
} // namespace Drv
|
||||
|
||||
@ -6,28 +6,29 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class DataBuffer : public Fw::SerializeBufferBase {
|
||||
public:
|
||||
|
||||
enum {
|
||||
DATA_BUFFER_SIZE = 256,
|
||||
SERIALIZED_TYPE_ID = 1010,
|
||||
SERIALIZED_SIZE = DATA_BUFFER_SIZE + sizeof(FwBuffSizeType)
|
||||
};
|
||||
|
||||
DataBuffer(const U8 *args, FwSizeType size);
|
||||
DataBuffer();
|
||||
DataBuffer(const DataBuffer& other);
|
||||
virtual ~DataBuffer();
|
||||
DataBuffer& operator=(const DataBuffer& other);
|
||||
|
||||
FwSizeType getBuffCapacity() const; // !< returns capacity, not current size, of buffer
|
||||
U8* getBuffAddr();
|
||||
const U8* getBuffAddr() const;
|
||||
|
||||
private:
|
||||
U8 m_data[DATA_BUFFER_SIZE]; // packet data buffer
|
||||
class DataBuffer : public Fw::SerializeBufferBase {
|
||||
public:
|
||||
enum {
|
||||
DATA_BUFFER_SIZE = 256,
|
||||
SERIALIZED_TYPE_ID = 1010,
|
||||
SERIALIZED_SIZE = DATA_BUFFER_SIZE + sizeof(FwBuffSizeType)
|
||||
};
|
||||
}
|
||||
|
||||
DataBuffer(const U8* args, FwSizeType size);
|
||||
DataBuffer();
|
||||
DataBuffer(const DataBuffer& other);
|
||||
virtual ~DataBuffer();
|
||||
DataBuffer& operator=(const DataBuffer& other);
|
||||
|
||||
DEPRECATED(FwSizeType getBuffCapacity() const, "Use getCapacity() instead");
|
||||
FwSizeType getCapacity() const;
|
||||
|
||||
U8* getBuffAddr();
|
||||
const U8* getBuffAddr() const;
|
||||
|
||||
private:
|
||||
U8 m_data[DATA_BUFFER_SIZE]; // packet data buffer
|
||||
};
|
||||
} // namespace Drv
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
module Drv {
|
||||
enum GpioStatus {
|
||||
enum GpioStatus : U8 {
|
||||
OP_OK @< Operation succeeded
|
||||
NOT_OPENED @< Pin was never opened
|
||||
INVALID_MODE @< Operation not permitted with current configuration
|
||||
|
||||
@ -11,7 +11,7 @@ module Drv {
|
||||
|
||||
module Drv {
|
||||
|
||||
enum I2cStatus {
|
||||
enum I2cStatus : U8 {
|
||||
I2C_OK = 0 @< Transaction okay
|
||||
I2C_ADDRESS_ERR = 1 @< I2C address invalid
|
||||
I2C_WRITE_ERR = 2 @< I2C write failed
|
||||
|
||||
@ -1,8 +1,28 @@
|
||||
module Drv {
|
||||
|
||||
port SpiWriteRead(
|
||||
ref writeBuffer: Fw.Buffer
|
||||
ref readBuffer: Fw.Buffer
|
||||
) -> Drv.SpiStatus
|
||||
|
||||
@ DEPRECATED: Use SpiWriteRead port instead (same operation with a return value)
|
||||
port SpiReadWrite(
|
||||
ref writeBuffer: Fw.Buffer
|
||||
ref readBuffer: Fw.Buffer
|
||||
ref readBuffer: Fw.Buffer
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
module Drv {
|
||||
|
||||
enum SpiStatus : U8 {
|
||||
SPI_OK = 0 @< Transaction okay
|
||||
SPI_OPEN_ERR = 1 @< SPI driver failed to open device
|
||||
SPI_CONFIG_ERR = 2 @< SPI read failed
|
||||
SPI_MISMATCH_ERR = 3 @< SPI read failed
|
||||
SPI_WRITE_ERR = 4 @< SPI write failed
|
||||
SPI_OTHER_ERR = 5 @< Other errors that do not fit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
namespace Drv {
|
||||
|
||||
typedef TcpClientComponentImpl TcpClient;
|
||||
typedef TcpClientComponentImpl TcpClient;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -10,30 +10,25 @@
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include <limits>
|
||||
#include <Drv/TcpClient/TcpClientComponentImpl.hpp>
|
||||
#include <Fw/FPrimeBasicTypes.hpp>
|
||||
#include <limits>
|
||||
#include "Fw/Types/Assert.hpp"
|
||||
|
||||
|
||||
namespace Drv {
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction, initialization, and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
TcpClientComponentImpl::TcpClientComponentImpl(const char* const compName)
|
||||
: TcpClientComponentBase(compName) {}
|
||||
TcpClientComponentImpl::TcpClientComponentImpl(const char* const compName) : TcpClientComponentBase(compName) {}
|
||||
|
||||
SocketIpStatus TcpClientComponentImpl::configure(const char* hostname,
|
||||
const U16 port,
|
||||
const U32 send_timeout_seconds,
|
||||
const U32 send_timeout_microseconds,
|
||||
FwSizeType buffer_size) {
|
||||
|
||||
// Check that ensures the configured buffer size fits within the limits fixed-width type, U32
|
||||
FW_ASSERT(buffer_size <= std::numeric_limits<U32>::max(), static_cast<FwAssertArgType>(buffer_size));
|
||||
m_allocation_size = buffer_size; // Store the buffer size
|
||||
m_allocation_size = buffer_size; // Store the buffer size
|
||||
return m_socket.configure(hostname, port, send_timeout_seconds, send_timeout_microseconds);
|
||||
}
|
||||
|
||||
@ -48,18 +43,16 @@ IpSocket& TcpClientComponentImpl::getSocketHandler() {
|
||||
}
|
||||
|
||||
Fw::Buffer TcpClientComponentImpl::getBuffer() {
|
||||
return allocate_out(0, static_cast<U32>(m_allocation_size));
|
||||
return allocate_out(0, m_allocation_size);
|
||||
}
|
||||
|
||||
void TcpClientComponentImpl::sendBuffer(Fw::Buffer buffer, SocketIpStatus status) {
|
||||
Drv::ByteStreamStatus recvStatus = ByteStreamStatus::OTHER_ERROR;
|
||||
if (status == SOCK_SUCCESS) {
|
||||
recvStatus = ByteStreamStatus::OP_OK;
|
||||
}
|
||||
else if (status == SOCK_NO_DATA_AVAILABLE) {
|
||||
} else if (status == SOCK_NO_DATA_AVAILABLE) {
|
||||
recvStatus = ByteStreamStatus::RECV_NO_DATA;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
recvStatus = ByteStreamStatus::OTHER_ERROR;
|
||||
}
|
||||
this->recv_out(0, buffer, recvStatus);
|
||||
@ -69,16 +62,14 @@ void TcpClientComponentImpl::connected() {
|
||||
if (isConnected_ready_OutputPort(0)) {
|
||||
this->ready_out(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TcpClientComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
FW_ASSERT_NO_OVERFLOW(fwBuffer.getSize(), U32);
|
||||
Drv::SocketIpStatus status = send(fwBuffer.getData(), static_cast<U32>(fwBuffer.getSize()));
|
||||
Drv::ByteStreamStatus TcpClientComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
Drv::SocketIpStatus status = send(fwBuffer.getData(), fwBuffer.getSize());
|
||||
Drv::ByteStreamStatus returnStatus;
|
||||
switch (status) {
|
||||
case SOCK_INTERRUPTED_TRY_AGAIN:
|
||||
@ -91,8 +82,7 @@ void TcpClientComponentImpl::send_handler(const FwIndexType portNum, Fw::Buffer&
|
||||
returnStatus = ByteStreamStatus::OTHER_ERROR;
|
||||
break;
|
||||
}
|
||||
// Return the buffer and status to the caller
|
||||
this->sendReturnOut_out(0, fwBuffer, returnStatus);
|
||||
return returnStatus;
|
||||
}
|
||||
|
||||
void TcpClientComponentImpl::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
|
||||
|
||||
@ -21,8 +21,7 @@
|
||||
namespace Drv {
|
||||
|
||||
class TcpClientComponentImpl final : public TcpClientComponentBase, public SocketComponentHelper {
|
||||
|
||||
friend class TcpClientTester;
|
||||
friend class TcpClientTester;
|
||||
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
@ -103,12 +102,10 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
|
||||
|
||||
/**
|
||||
* \brief called when the IPv4 system has been connected
|
||||
*/
|
||||
*/
|
||||
void connected() override;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler implementations for user-defined typed input ports
|
||||
// ----------------------------------------------------------------------
|
||||
@ -127,17 +124,16 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
|
||||
* \param portNum: fprime port number of the incoming port call
|
||||
* \param fwBuffer: buffer containing data to be sent
|
||||
*/
|
||||
void send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
||||
Drv::ByteStreamStatus send_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) override;
|
||||
|
||||
//! Handler implementation for recvReturnIn
|
||||
//!
|
||||
//! Port receiving back ownership of data sent out on $recv port
|
||||
void recvReturnIn_handler(FwIndexType portNum, //!< The port number
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
Fw::Buffer& fwBuffer //!< The buffer
|
||||
) override;
|
||||
|
||||
|
||||
Drv::TcpClientSocket m_socket; //!< Socket implementation
|
||||
Drv::TcpClientSocket m_socket; //!< Socket implementation
|
||||
|
||||
// Member variable to store the buffer size
|
||||
FwSizeType m_allocation_size;
|
||||
@ -145,4 +141,4 @@ class TcpClientComponentImpl final : public TcpClientComponentBase, public Socke
|
||||
|
||||
} // end namespace Drv
|
||||
|
||||
#endif // end TcpClientComponentImpl
|
||||
#endif // end TcpClientComponentImpl
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
#include "TcpClientTester.hpp"
|
||||
|
||||
|
||||
TEST(Nominal, TcpClientBasicMessaging) {
|
||||
Drv::TcpClientTester tester;
|
||||
tester.test_basic_messaging();
|
||||
|
||||
@ -10,10 +10,10 @@
|
||||
//
|
||||
// ======================================================================
|
||||
#include "TcpClientTester.hpp"
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
#include <Os/Console.hpp>
|
||||
#include <Drv/Ip/test/ut/PortSelector.hpp>
|
||||
#include <Drv/Ip/test/ut/SocketTestHelper.hpp>
|
||||
#include <Os/Console.hpp>
|
||||
#include "STest/Pick/Pick.hpp"
|
||||
|
||||
Os::Console logger;
|
||||
|
||||
@ -23,18 +23,20 @@ namespace Drv {
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TcpClientTester ::setup_helper(Drv::TcpServerSocket& server, Drv::SocketDescriptor& server_fd, bool recv_thread, bool reconnect) {
|
||||
void TcpClientTester ::setup_helper(Drv::TcpServerSocket& server,
|
||||
Drv::SocketDescriptor& server_fd,
|
||||
bool recv_thread,
|
||||
bool reconnect) {
|
||||
Drv::SocketIpStatus serverStat = Drv::SOCK_SUCCESS;
|
||||
|
||||
U16 port = 0;
|
||||
U16 port = 0;
|
||||
server.configure("127.0.0.1", port, 0, 100);
|
||||
|
||||
serverStat = server.startup(server_fd);
|
||||
this->component.configure("127.0.0.1", server.getListenPort(), 0, 100);
|
||||
|
||||
ASSERT_EQ(serverStat, SOCK_SUCCESS)
|
||||
<< "TCP server startup error: " << strerror(errno) << std::endl
|
||||
<< "Port: " << port << std::endl;
|
||||
ASSERT_EQ(serverStat, SOCK_SUCCESS) << "TCP server startup error: " << strerror(errno) << std::endl
|
||||
<< "Port: " << port << std::endl;
|
||||
|
||||
// Start up a receive thread
|
||||
if (recv_thread) {
|
||||
@ -44,7 +46,6 @@ void TcpClientTester ::setup_helper(Drv::TcpServerSocket& server, Drv::SocketDes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
||||
U8 buffer[sizeof(m_data_storage)] = {};
|
||||
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
||||
@ -56,13 +57,14 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
||||
|
||||
// Loop through a bunch of client disconnects
|
||||
for (U32 i = 0; i < iterations; i++) {
|
||||
U32 size = sizeof(m_data_storage);
|
||||
FwSizeType size = sizeof(m_data_storage);
|
||||
|
||||
// Not testing with reconnect thread, we will need to open ourselves
|
||||
if (not recv_thread) {
|
||||
status1 = this->component.open();
|
||||
} else {
|
||||
EXPECT_TRUE(this->wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1));
|
||||
EXPECT_TRUE(this->wait_on_change(this->component.getSocketHandler(), true,
|
||||
Drv::Test::get_configured_delay_ms() / 10 + 1));
|
||||
}
|
||||
EXPECT_TRUE(this->component.isOpened());
|
||||
// fd has now been updated to be a value we need to keep track of
|
||||
@ -72,16 +74,13 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
||||
|
||||
// If all the opens worked, then run this
|
||||
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
||||
(this->component.isOpened())) {
|
||||
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) && (this->component.isOpened())) {
|
||||
// Force the sockets not to hang, if at all possible
|
||||
Drv::Test::force_recv_timeout(this->component.m_descriptor.fd, this->component.getSocketHandler());
|
||||
Drv::Test::force_recv_timeout(server_fd.serverFd, server);
|
||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||
invoke_to_send(0, m_data_buffer);
|
||||
ASSERT_from_sendReturnOut_SIZE(i + 1);
|
||||
Drv::ByteStreamStatus status = this->fromPortHistory_sendReturnOut->at(i).status;
|
||||
Drv::ByteStreamStatus status = invoke_to_send(0, m_data_buffer);
|
||||
EXPECT_EQ(status, ByteStreamStatus::OP_OK);
|
||||
Drv::Test::receive_all(server, server_fd, buffer, size);
|
||||
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
||||
@ -91,7 +90,8 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||
status2 = server.send(server_fd, m_data_buffer.getData(), m_data_buffer.getSize());
|
||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
||||
while (not m_spinner) {}
|
||||
while (not m_spinner) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Properly stop the client on the last iteration
|
||||
@ -115,7 +115,8 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
||||
TcpClientTester ::TcpClientTester()
|
||||
: TcpClientGTestBase("Tester", MAX_HISTORY_SIZE),
|
||||
component("TcpClient"),
|
||||
m_data_buffer(m_data_storage, 0), m_spinner(true) {
|
||||
m_data_buffer(m_data_storage, 0),
|
||||
m_spinner(true) {
|
||||
this->initComponents();
|
||||
this->connectPorts();
|
||||
::memset(m_data_storage, 0, sizeof(m_data_storage));
|
||||
@ -123,7 +124,7 @@ TcpClientTester ::TcpClientTester()
|
||||
|
||||
TcpClientTester ::~TcpClientTester() {}
|
||||
|
||||
bool TcpClientTester::wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations) {
|
||||
bool TcpClientTester::wait_on_change(Drv::IpSocket& socket, bool open, U32 iterations) {
|
||||
for (U32 i = 0; i < iterations; i++) {
|
||||
if (open == this->component.isOpened()) {
|
||||
return true;
|
||||
@ -137,7 +138,6 @@ bool TcpClientTester::wait_on_change(Drv::IpSocket &socket, bool open, U32 itera
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
|
||||
void TcpClientTester ::test_basic_messaging() {
|
||||
test_with_loop(1);
|
||||
}
|
||||
@ -151,7 +151,7 @@ void TcpClientTester ::test_receive_thread() {
|
||||
}
|
||||
|
||||
void TcpClientTester ::test_advanced_reconnect() {
|
||||
test_with_loop(10, true); // Up to 10 * RECONNECT_MS
|
||||
test_with_loop(10, true); // Up to 10 * RECONNECT_MS
|
||||
}
|
||||
|
||||
void TcpClientTester ::test_no_automatic_send_connection() {
|
||||
@ -171,7 +171,8 @@ void TcpClientTester ::test_no_automatic_recv_connection() {
|
||||
Drv::SocketDescriptor server_fd;
|
||||
this->setup_helper(server, server_fd, true, false);
|
||||
// Wait for connection to not start
|
||||
EXPECT_FALSE(this->wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms()/10 + 1));
|
||||
EXPECT_FALSE(
|
||||
this->wait_on_change(this->component.getSocketHandler(), true, Drv::Test::get_configured_delay_ms() / 10 + 1));
|
||||
ASSERT_FALSE(this->component.isOpened());
|
||||
// Clean-up even if the thread (incorrectly) started
|
||||
this->component.stop();
|
||||
@ -184,7 +185,7 @@ void TcpClientTester ::test_buffer_deallocation() {
|
||||
U8 data[1];
|
||||
Fw::Buffer buffer(data, sizeof(data));
|
||||
this->invoke_to_recvReturnIn(0, buffer);
|
||||
ASSERT_from_deallocate_SIZE(1); // incoming buffer should be deallocated
|
||||
ASSERT_from_deallocate_SIZE(1); // incoming buffer should be deallocated
|
||||
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getData(), data);
|
||||
ASSERT_EQ(this->fromPortHistory_deallocate->at(0).fwBuffer.getSize(), sizeof(data));
|
||||
}
|
||||
@ -193,15 +194,11 @@ void TcpClientTester ::test_buffer_deallocation() {
|
||||
// Handler overrides for typed from ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void TcpClientTester ::
|
||||
from_recv_handler(
|
||||
const FwIndexType portNum,
|
||||
Fw::Buffer &recvBuffer,
|
||||
const ByteStreamStatus &ByteStreamStatus
|
||||
)
|
||||
{
|
||||
void TcpClientTester ::from_recv_handler(const FwIndexType portNum,
|
||||
Fw::Buffer& recvBuffer,
|
||||
const ByteStreamStatus& ByteStreamStatus) {
|
||||
this->pushFromPortEntry_recv(recvBuffer, ByteStreamStatus);
|
||||
if (ByteStreamStatus == ByteStreamStatus::OP_OK){
|
||||
if (ByteStreamStatus == ByteStreamStatus::OP_OK) {
|
||||
// Make sure we can get to unblocking the spinner
|
||||
EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size";
|
||||
Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData());
|
||||
@ -211,16 +208,11 @@ void TcpClientTester ::test_buffer_deallocation() {
|
||||
delete[] recvBuffer.getData();
|
||||
}
|
||||
|
||||
Fw::Buffer TcpClientTester ::
|
||||
from_allocate_handler(
|
||||
const FwIndexType portNum,
|
||||
FwSizeType size
|
||||
)
|
||||
{
|
||||
Fw::Buffer TcpClientTester ::from_allocate_handler(const FwIndexType portNum, FwSizeType size) {
|
||||
this->pushFromPortEntry_allocate(size);
|
||||
Fw::Buffer buffer(new U8[size], size);
|
||||
m_data_buffer2 = buffer;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace Drv
|
||||
|
||||
@ -13,115 +13,103 @@
|
||||
#ifndef TESTER_HPP
|
||||
#define TESTER_HPP
|
||||
|
||||
#include "TcpClientGTestBase.hpp"
|
||||
#include "Drv/TcpClient/TcpClientComponentImpl.hpp"
|
||||
#include "Drv/Ip/TcpServerSocket.hpp"
|
||||
#include "Drv/TcpClient/TcpClientComponentImpl.hpp"
|
||||
#include "TcpClientGTestBase.hpp"
|
||||
|
||||
#define SEND_DATA_BUFFER_SIZE 1024
|
||||
|
||||
namespace Drv {
|
||||
|
||||
class TcpClientTester :
|
||||
public TcpClientGTestBase
|
||||
{
|
||||
// Maximum size of histories storing events, telemetry, and port outputs
|
||||
static const U32 MAX_HISTORY_SIZE = 1000;
|
||||
// Instance ID supplied to the component instance under test
|
||||
static const FwEnumStoreType TEST_INSTANCE_ID = 0;
|
||||
// Queue depth supplied to component instance under test
|
||||
static const FwSizeType TEST_INSTANCE_QUEUE_DEPTH = 100;
|
||||
class TcpClientTester : public TcpClientGTestBase {
|
||||
// Maximum size of histories storing events, telemetry, and port outputs
|
||||
static const FwSizeType MAX_HISTORY_SIZE = 1000;
|
||||
// Instance ID supplied to the component instance under test
|
||||
static const FwEnumStoreType TEST_INSTANCE_ID = 0;
|
||||
// Queue depth supplied to component instance under test
|
||||
static const FwSizeType TEST_INSTANCE_QUEUE_DEPTH = 100;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------
|
||||
// Construction and destruction
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
public:
|
||||
//! Construct object TcpClientTester
|
||||
//!
|
||||
TcpClientTester();
|
||||
|
||||
//! Construct object TcpClientTester
|
||||
//!
|
||||
TcpClientTester();
|
||||
//! Destroy object TcpClientTester
|
||||
//!
|
||||
~TcpClientTester();
|
||||
|
||||
//! Destroy object TcpClientTester
|
||||
//!
|
||||
~TcpClientTester();
|
||||
public:
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
void setup_helper(Drv::TcpServerSocket& server, Drv::SocketDescriptor& server_fd, bool recv_thread, bool reconnect);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
void test_basic_messaging();
|
||||
|
||||
void setup_helper(Drv::TcpServerSocket& server, Drv::SocketDescriptor& server_fd, bool recv_thread, bool reconnect);
|
||||
void test_multiple_messaging();
|
||||
|
||||
void test_basic_messaging();
|
||||
void test_receive_thread();
|
||||
|
||||
void test_multiple_messaging();
|
||||
void test_advanced_reconnect();
|
||||
|
||||
void test_receive_thread();
|
||||
void test_no_automatic_send_connection();
|
||||
|
||||
void test_advanced_reconnect();
|
||||
void test_no_automatic_recv_connection();
|
||||
|
||||
void test_no_automatic_send_connection();
|
||||
void test_with_loop(U32 iterations, bool recv_thread = false);
|
||||
|
||||
void test_no_automatic_recv_connection();
|
||||
void test_buffer_deallocation();
|
||||
|
||||
void test_with_loop(U32 iterations, bool recv_thread=false);
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler overrides for typed from ports
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void test_buffer_deallocation();
|
||||
//! Handler for from_recv
|
||||
//!
|
||||
void from_recv_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer& recvBuffer,
|
||||
const ByteStreamStatus& ByteStreamStatus) override;
|
||||
|
||||
private:
|
||||
//! Handler for from_allocate
|
||||
//!
|
||||
Fw::Buffer from_allocate_handler(const FwIndexType portNum, /*!< The port number*/
|
||||
FwSizeType size) override;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Handler overrides for typed from ports
|
||||
// ----------------------------------------------------------------------
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! Handler for from_recv
|
||||
//!
|
||||
void from_recv_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
Fw::Buffer &recvBuffer,
|
||||
const ByteStreamStatus &ByteStreamStatus
|
||||
) override;
|
||||
bool wait_on_change(Drv::IpSocket& socket, bool open, U32 iterations);
|
||||
|
||||
//! Handler for from_allocate
|
||||
//!
|
||||
Fw::Buffer from_allocate_handler(
|
||||
const FwIndexType portNum, /*!< The port number*/
|
||||
FwSizeType size
|
||||
) override;
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
private:
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
private:
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool wait_on_change(Drv::IpSocket &socket, bool open, U32 iterations);
|
||||
//! The component under test
|
||||
//!
|
||||
TcpClientComponentImpl component;
|
||||
Fw::Buffer m_data_buffer;
|
||||
Fw::Buffer m_data_buffer2;
|
||||
U8 m_data_storage[SEND_DATA_BUFFER_SIZE];
|
||||
std::atomic<bool> m_spinner;
|
||||
};
|
||||
|
||||
//! Connect ports
|
||||
//!
|
||||
void connectPorts();
|
||||
|
||||
//! Initialize components
|
||||
//!
|
||||
void initComponents();
|
||||
|
||||
private:
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Variables
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
//! The component under test
|
||||
//!
|
||||
TcpClientComponentImpl component;
|
||||
Fw::Buffer m_data_buffer;
|
||||
Fw::Buffer m_data_buffer2;
|
||||
U8 m_data_storage[SEND_DATA_BUFFER_SIZE];
|
||||
std::atomic<bool> m_spinner;
|
||||
|
||||
};
|
||||
|
||||
} // end namespace Drv
|
||||
} // end namespace Drv
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user