Compare commits

...

16 Commits

Author SHA1 Message Date
mathis_s 308747b747 Reset error during each poll
Test And Build / Build (push) Successful in 7s
Test And Build / Lint (push) Failing after 7s
2026-05-27 13:58:11 +02:00
wall_e 3339f5a3a3 human readable tcpdump
Test And Build / Build (push) Successful in 7s
Test And Build / Lint (push) Failing after 7s
2026-05-04 17:05:20 +02:00
wall_e 683fa2138f adds gitea action
Test And Build / Lint (push) Failing after 7s
Test And Build / Build (push) Successful in 7s
2025-07-04 14:35:07 +02:00
mathis_s 354e9d90fb Fixed missing initializer for variables in Phytron-Axis 2025-06-20 13:46:10 +02:00
soederqvist_a deea821e3f Merge pull request 'el734' (#2) from el734 into master
Reviewed-on: #2
2025-06-11 15:07:19 +02:00
soederqvist_a 7a46788fd5 Install a el734 db file
also remove explicit libversion in the makefile
2025-06-11 15:05:14 +02:00
mathis_s 9e77eb585c Merge branch 'lift_axis_no_autoenable' 2025-04-17 17:00:19 +02:00
mathis_s 7e1fc78f76 Moved curses from top-level import to function-level import and added a
comment why that is necessary
2024-10-24 10:49:18 +02:00
mathis_s 9e0d8a4322 Added a new script utils/decodeMasterMACStatusR10.py which allows to
decode the R10 status message of the MasterMACs controller.

Also fixed a bug in utils/deltatau.py (error when printing too much text
at once)
2024-10-24 10:34:19 +02:00
mathis_s 3cccfe930c Removed typo from C804Axis.cpp 2024-10-18 09:53:47 +02:00
mathis_s 8860d0c59f Updated the first-time-poll of C804 Axis 2024-10-18 09:48:17 +02:00
mathis_s b6c38be113 Initial driver version for the C804 controller 2024-10-18 09:48:17 +02:00
wall_e b14b50c25a Merge branch 'can-we-have-pipelines' into 'master'
Adds CI-Pipeline with Formatting, Linter Checks and Build Steps

See merge request sinqdev/sinqepicsapp!4
2024-10-14 10:07:16 +02:00
wall_e 477ffdbc0b Adds CI-Pipeline with Formatting, Linter Checks and Build Steps 2024-10-14 10:07:16 +02:00
wall_e 0a23ec8f22 clang is too old 2024-10-10 13:22:09 +02:00
wall_e 39098fd0d1 Adds .clang-format style for formatting files 2024-09-25 16:21:01 +02:00
11 changed files with 688 additions and 6 deletions
+236
View File
@@ -0,0 +1,236 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseColons: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Attach
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
KeepEmptyLinesAtEOF: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: BinPack
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
PPIndentWidth: -1
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
...
+21
View File
@@ -0,0 +1,21 @@
name: Test And Build
on: [push]
jobs:
Lint:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
- name: cppcheck
run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 sinqEPICSApp/src/*.cpp
- name: formatting
run: clang-format --style=file --Werror --dry-run sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h
Build:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
- run: |
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile.RHEL8
make -f Makefile.RHEL8 install
+5 -4
View File
@@ -4,7 +4,7 @@ include /ioc/tools/driver.makefile
MODULE=sinq
BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL%
ARCH_FILTER=RHEL8%
# additional module dependencies
REQUIRED+=SynApps
@@ -12,17 +12,18 @@ REQUIRED+=stream
REQUIRED+=scaler
REQUIRED+=motorBase
# Release version
LIBVERSION=2025
# DB files to include in the release
TEMPLATES += sinqEPICSApp/Db/dimetix.db
TEMPLATES += sinqEPICSApp/Db/slsvme.db
TEMPLATES += sinqEPICSApp/Db/spsamor.db
TEMPLATES += sinqEPICSApp/Db/el734.db
# DBD files to include in the release
DBDS += sinqEPICSApp/src/sinq.dbd
# Release version
LIBVERSION=2026
# Source files to build
SOURCES += sinqEPICSApp/src/devScalerEL737.c
SOURCES += sinqEPICSApp/src/SINQController.cpp
+9
View File
@@ -38,3 +38,12 @@ Those political problems require a special development model:
Take care of the sinqEPICsApp/src/sinq.dbd file. This is the one which differs mostly between
amorsim and master branches.
# Formatting
Formatting is done via the [`.clang-format`](./.clang-format) file checked into
the repository. One option to apply the formatting to a given file is via the
command below.
```
clang-format -i -style=file <file>
```
+34
View File
@@ -0,0 +1,34 @@
record(motor,"$(P)$(M)")
{
field(DESC,"$(DESC)")
field(DTYP,"$(DTYP)")
field(DIR,"$(DIR)")
field(VELO,"$(VELO)")
field(HVEL,"$(VELO)")
field(VBAS,"$(VELO)")
field(VMAX, "${VMAX}")
field(ACCL,"$(ACCL)")
field(BDST,"$(BDST)")
field(BVEL,"$(BVEL)")
field(BACC,"$(BACC)")
field(OUT,"@asyn($(PORT),$(ADDR))")
field(MRES,"$(MRES)")
field(PREC,"$(PREC)")
field(EGU,"$(EGU)")
field(DHLM,"$(DHLM)")
field(DLLM,"$(DLLM)")
field(INIT,"$(INIT)")
field(PINI, "NO")
field(TWV,"1")
field(RTRY,"0")
}
# The message text
record(waveform, "$(P)$(M)-MsgTxt") {
field(DTYP, "asynOctetRead")
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
field(FTVL, "CHAR")
field(NELM, "80")
field(SCAN, "I/O Intr")
}
+4 -1
View File
@@ -505,7 +505,10 @@ asynStatus EL734Axis::poll(bool *moving)
// errlogPrintf("Axis %d, reply %s, msr %d, oredmsr = %d, position = %lf\n",
// axisNo_, reply, msr, oredMSR, position);
oredMSR |= msr;
// Reset the error during each poll. This is necessary because some errors
// apparently don't get cleared by the controller (especially "lower / higher
// limit hit").
oredMSR = msr;
if ((msr & 0x1) == 0)
{
// done: check for trouble
+2
View File
@@ -250,6 +250,8 @@ PhytronAxis::PhytronAxis(PhytronController *pC, int axisNo, int enc)
haveBrake = 0;
brakeIO = -1;
next_poll = -1;
homing = 0;
homing_direction = 0;
}
int PhytronAxis::setBrake(int brakeNO)
+1 -1
View File
@@ -114,7 +114,7 @@ typedef struct {
unsigned int dbInit;
}EL737priv;
static void dummyAsynCallback([[maybe_unused]] asynUser *pasynUser)
static void dummyAsynCallback(asynUser *pasynUser)
{
}
+213
View File
@@ -0,0 +1,213 @@
#!/usr/bin/env python3
# List of tuples which encodes the states given in the file description.
# Index first with the bit index, then with the bit value
interpretation = [
("Not ready to be switched on", "Ready to be switched on"), # Bit 0
("Not switched on", "Switched on"), # Bit 1
("Disabled", "Enabled"), # Bit 2
("Ok", "Fault condition set"), # Bit 3
("Motor supply voltage absent ", "Motor supply voltage present"), # Bit 4
("Motor performs quick stop", "Ok"), # Bit 5
("Switch on enabled", "Switch on disabled"), # Bit 6
("Ok", "RWarning: Movement function was called while motor is still moving. The function call is ignored"), # Bit 7
("Motor is idle", "Motor is currently moving"), # Bit 8
("Motor does not execute command messages (local mode)", "Motor does execute command messages (remote mode)"), # Bit 9
("Target not reached", "Target reached"), # Bit 10
("Ok", "Internal limit active"), # Bit 11
("Not specified", "Not specified"), # Bit 12
("Not specified", "Not specified"), # Bit 13
("No event set or event has not occurred yet", "Set event has occurred"), # Bit 14
("Axis off (power disabled)", "Axis on (power enabled)"), # Bit 15
]
def decode(value, big_endian: bool = False):
interpreted = []
bit_list = [(value >> shift_ind) & 1
for shift_ind in range(value.bit_length())] # little endian
if big_endian:
bit_list.reverse() # big endian
for (bit, interpretations) in zip(bit_list, interpretation):
interpreted.append(interpretations[bit])
return (bit_list, interpreted)
def print_decoded(bit_list, interpreted):
for (idx, (bit_value, msg)) in enumerate(zip(bit_list, interpreted)):
print(f"Bit {idx} = {bit_value}: {msg}")
def interactive():
# Imported here, because curses is not available in Windows. Using the
# interactive mode therefore fails on Windows, but at least the single
# command mode can be used (which would not be possible if we would import
# curses at the top level)
import curses
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
stdscr.scrollok(True)
stdscr.addstr(">> ")
stdscr.refresh()
history = [""]
ptr = len(history) - 1
while True:
c = stdscr.getch()
if c == curses.KEY_RIGHT:
(y, x) = stdscr.getyx()
if x < len(history[ptr]) + 3:
stdscr.move(y, x+1)
stdscr.refresh()
elif c == curses.KEY_LEFT:
(y, x) = stdscr.getyx()
if x > 3:
stdscr.move(y, x-1)
stdscr.refresh()
elif c == curses.KEY_UP:
if ptr > 0:
ptr -= 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_DOWN:
if ptr < len(history) - 1:
ptr += 1
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
elif c == curses.KEY_ENTER or c == ord('\n') or c == ord('\r'):
if history[ptr] == 'quit':
break
# because of arrow keys move back to the end of the line
(y, x) = stdscr.getyx()
stdscr.move(y, 3+len(history[ptr]))
if history[ptr]:
result = interpret_inputs(history[ptr].split())
if result is None:
stdscr.addstr(f"\nBAD INPUT: Expected input of 'value [big_endian]', where 'value' is an int or a float and 'big_endian' is an optional boolean argument.")
else:
(arg, big_endian) = result
(bit_list, interpreted) = decode(arg, big_endian)
for (idx, (bit_value, msg)) in enumerate(zip(bit_list, interpreted)):
stdscr.addstr(f"\nBit {idx} = {bit_value}: {msg}")
stdscr.refresh()
if ptr == len(history) - 1 and history[ptr] != "":
history += [""]
else:
history[-1] = ""
ptr = len(history) - 1
stdscr.addstr("\n>> ")
stdscr.refresh()
else:
if ptr < len(history) - 1: # Modifying previous input
if len(history[-1]) == 0:
history[-1] = history[ptr]
ptr = len(history) - 1
else:
history += [history[ptr]]
ptr = len(history) - 1
if c == curses.KEY_BACKSPACE:
if len(history[ptr]) == 0:
continue
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-4] + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x-1)
stdscr.refresh()
else:
(y, x) = stdscr.getyx()
history[ptr] = history[ptr][0:x-3] + chr(c) + history[ptr][x-3:]
stdscr.addch("\r")
stdscr.clrtoeol()
stdscr.addstr(">> " + history[ptr])
stdscr.move(y, x+1)
stdscr.refresh()
# to quit
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
def interpret_inputs(inputs):
number = None
big_endian = False
try:
number = int(float(inputs[0]))
if len(inputs) > 1:
second_arg = inputs[1]
if second_arg == "True" or second_arg == "true":
big_endian = True
elif second_arg == "False" or second_arg == "false":
big_endian = False
else:
big_endian = bool(int(second_arg))
return (number, big_endian)
except:
return None
if __name__ == "__main__":
from sys import argv
if len(argv) == 1:
# Start interactive mode
interactive()
else:
result = interpret_inputs(argv[1:])
if result is None:
print("""
Decode R10 message of MasterMACs
------------------
MasterMACs returns its status message (R10) as a floating-point number.
The bits of this float encode different states. These states are stored
in the interpretation variable.
This script can be used in two different ways:
Option 1: Single Command
------------------------
Usage: decodeMasterMACStatusR10.py value [big_endian]
'value' is the return value of a R10 command. This value is interpreted
bit-wise and the result is printed out. The optional second argument can
be used to specify whether the input value needs to be interpreted as
little or big endian. Default is False.
Option 2: CLI Mode
------------------
Usage: decodeMasterMACStatusR10.py
A prompt will be opened. Type in the return value of a R10 command, hit
enter and the interpretation will be printed in the prompt. After that,
the next value can be typed in. Type 'quit' to close the prompt.
""")
else:
print("Motor status")
print("============")
(arg, big_endian) = result
(bit_list, interpreted) = decode(arg, big_endian)
print_decoded(bit_list, interpreted)
+1
View File
@@ -64,6 +64,7 @@ if __name__ == "__main__":
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
stdscr.scrollok(True)
stdscr.addstr(">> ")
stdscr.refresh()
+162
View File
@@ -0,0 +1,162 @@
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
################################################################################
# This converts the hex form of a wireshark analysis follow to something
# more human readable.
#
# It is only partially implemented!
################################################################################
f_name="${1}"
if [ "$#" -eq 2 ]; then
filt="${2}"
fi
declare -A POSITION_MODE
POSITION_MODE[1]="RELATIVE"
POSITION_MODE[2]="ABSOLUTE"
POSITION_MODE[3]="INTERNAL_REFERANCE"
POSITION_MODE[4]="EXTERNAL_REFERANCE"
declare -A COMMANDS
function to_ord() {
printf "%x" "'$1"
}
function add_char() {
COMMANDS["$(to_ord "$1")"]="$2"
}
function add_ord() {
COMMANDS["$1"]="$2"
}
add_char A Start_Movement
add_char C Get_Position
add_char D Set_Position_And_Clear_Error
add_char d Set_Direction
add_char p Set_Position_Mode
add_char '$' Get_Status
add_char 0 0
add_char 1 1
add_char 2 2
add_char 3 3
add_char 4 4
add_char 5 5
add_char 6 6
add_char 7 7
add_char 8 8
add_char 9 9
add_char '#' '#'
add_ord 00 ''
add_ord 0d '' # '\r'
add_ord 2b '+'
add_ord 2d '-'
while read -r line; do
fields="$( echo "${line}" | sed -e 's/^ *//' -e 's/ /:/' -e 's/ /:/' | tr -s ' ' )"
IFS=' ' bytes=($( echo "${fields}" | cut -d':' -f2))
string="$( echo "${fields}" | cut -d':' -f3)"
parsing_number=-1
num_len=0
is_negative=0
human_readable=()
for byte in "${bytes[@]}"; do
if [[ -v COMMANDS["${byte}"] ]]; then
if [[ "${COMMANDS["${byte}"]}" == Get_Status ]] || [[ "${COMMANDS["${byte}"]}" == Get_Position ]] || [[ "${COMMANDS["${byte}"]}" == Set_Position_Mode ]] || [[ "${COMMANDS["${byte}"]}" == Set_Position_And_Clear_Error ]]; then
parsing_number=0
numlen=0
is_negative=0
elif [[ "${parsing_number}" -ge 0 ]] && ( [[ "${byte}" == 00 ]] || [[ "${byte}" == 0d ]] ); then
if [[ "${num_len}" -ge 1 ]]; then
if [[ "${human_readable[${#human_readable[@]}-1]}" == Get_Status ]]; then
if [[ "$(( parsing_number & 2#0001 ))" -eq 1 ]]; then
human_readable=("${human_readable[@]}" "READY")
fi
if [[ "$(( parsing_number & 2#0010 ))" -eq 1 ]]; then
human_readable=("${human_readable[@]}" "ZERO-POS_REACHED")
fi
if [[ "$(( parsing_number & 2#0100 ))" -eq 1 ]]; then
human_readable=("${human_readable[@]}" "POSITIONING_ERROR")
fi
if [[ "$(( parsing_number & 2#1000 ))" -eq 1 ]]; then
human_readable=("${human_readable[@]}" "SOMETHING?")
fi
elif [[ "${human_readable[${#human_readable[@]}-1]}" == Set_Position_Mode ]]; then
human_readable=("${human_readable[@]}" "${POSITION_MODE[${parsing_number}]}")
else
if [[ "${is_negative}" -eq 1 ]]; then
human_readable=("${human_readable[@]}" "$(( parsing_number * -1))")
else
human_readable=("${human_readable[@]}" "${parsing_number}")
fi
fi
fi
parsing_number=-1
continue
elif [ "${parsing_number}" -ge 0 ]; then
if [[ "${COMMANDS["${byte}"]}" == '-' ]]; then
is_negative=1
elif [[ "${COMMANDS["${byte}"]}" == '+' ]]; then
is_negative=0
else
parsing_number="$(( parsing_number * 10 + "${COMMANDS["${byte}"]}" ))"
fi
num_len=$(( num_len + 1 ))
continue
fi
if [ "${#human_readable[@]}" -gt 0 ]; then
human_readable=("${human_readable[@]}" "${COMMANDS["${byte}"]}")
else
human_readable=("${COMMANDS["${byte}"]}")
fi
else
if [ "${#human_readable[@]}" -gt 0 ]; then
human_readable=("${human_readable[@]}" "${byte}(unknown)")
else
human_readable=("${byte}(unknown)")
fi
fi
done
if [[ "${human_readable[0]}" == '#' ]]; then
if [[ -z "${filt+x}" ]]; then
echo "Sent: ${human_readable[@]}"
else
if [[ "${human_readable[1]}" == "${filt}" ]]; then
echo "Sent: ${human_readable[@]}"
fi
fi
else
# Some commands prefix the response with two 0's (I assume there can be
# more axes)
if [[ "${human_readable[0]}" == 0 ]] && [[ "${human_readable[1]}" == 0 ]]; then
IFS=' ' human_readable=("${human_readable[@]:2}")
fi
if [[ -z "${filt+x}" ]]; then
echo "Received: ${human_readable[@]}"
else
if [[ "${human_readable[0]}" == "${filt}" ]]; then
echo "Received: ${human_readable[@]}"
fi
fi
fi
done < "${f_name}"