-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(example): Generic synchronization example #242
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for getting this example together.
Looks like flake8 caught some of the problems and failed the Travis CI, please fix those. I'd recommend running tox locally before pushing.
Few more things:
- We should add a unit test for this. See Add CAN and LIN database creation examples #219 for an example on how to do this. Feel free to reach out to me if you have any questions.
- We should update/add rst files for documentation (for sphinx to generate the appropriate html) Add CAN and LIN database creation examples #219 also has examples of this.
synchronized_input.intf.can_term = constants.CanTerm.ON | ||
|
||
#Connect the start trigger terminals for the synchronized interface | ||
synchronized_input.connect_terminals(nixnet._cconsts.NX_TERM_FRONT_PANEL0, nixnet._cconsts.NX_TERM_START_TRIGGER) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nixnet._cconsts
is internal-only. Consider making these constants public. For ideas, see constants.py
for frame in receivedFrames: | ||
rxTimestamp = frame.timestamp | ||
print('Received frame: {}'.format(frame)) | ||
print(' payload={}'.format(list(six.iterbytes(frame.payload)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if the loop is needed here. Looks like we're just writing one frame at line 72.
Even if we modified the example to write multiple frames, I'm not sure if we should overwrite the timestamps this way.
Ditto for echoedFrames
inp = six.moves.input('Hit enter to continue (q to quit): ') | ||
if inp.lower() == 'q': | ||
break | ||
print('Data acquisition stopped.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're likely leaking memory and/or not giving up session resources here (because SessionBase.close
never got called). For reasons mentioned in an issue above, we should encourage the use of with-statements when creating XNET sessions.
|
||
#Create the objects for the synchronized interface | ||
synchronized_input = nixnet.FrameInStreamSession(interface1) | ||
synchronized_output = nixnet.FrameOutStreamSession(interface1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use a with-statement when creating XNET objects because it's context manager is responsible for invoking the built-in __enter__
/ __exit__
functions at the appropriate times. For XNET sessions in particular, we call close
during __exit__
which is responsible for giving up resources and cleaning up memory.
I noticed you're creating four sessions, in which case nesting with-statements can make the code hard to read. You could use ExitStack for combining other context managers and cleanup functions. Let me know if you have other ideas or questions about using ExitStack.
|
||
#Create the session objects for the synchronizing interface | ||
synchronizing_input = nixnet.FrameInStreamSession(interface2) | ||
synchronizing_output = nixnet.FrameOutStreamSession(interface2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are synchronizing
and synchronized
names appropriate here? I don't know enough about session synchronization to know what's suitable here. Would original_input and echo_input make more sense?
Also, is synchronized_output
being used anywhere? I'm wondering if we could get away with just 3 sessions for this example.
nixnet/_enums.py
Outdated
@@ -2029,3 +2029,26 @@ class FrameType(enum.Enum): | |||
SPECIAL_DELAY = _cconsts.NX_FRAME_TYPE_SPECIAL_DELAY | |||
SPECIAL_LOG_TRIGGER = _cconsts.NX_FRAME_TYPE_SPECIAL_LOG_TRIGGER | |||
SPECIAL_START_TRIGGER = _cconsts.NX_FRAME_TYPE_SPECIAL_START_TRIGGER | |||
|
|||
class Terminals(enum.Enum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the constants are prefixed with TERM
, should this enum be called Term
?
iirc that was how I decided the name for all of the other enums
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Term" is ambiguous since we have both "Termination" and "Terminal" in the mix. For readability improvements mentioned below I suggest dropping the NX and spelling out terminal i.e TERMINAL_FRONTPANEL0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, iirc we don't tend to name the enums as a plural, so if it is expanded, it'd be Terminal
.
nixnet/_enums.py
Outdated
|
||
class Terminals(enum.Enum): | ||
"Terminals to import/export from the XNET device" | ||
NX_TERM_FRONTPANEL0 = _cconsts.NX_TERM_FRONT_PANEL0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Because these are in an enum, we shouldn't have the
NX_TERM
/NXTERM
prefixes - Why did
FRONT_PANEL0
get turned intoFRONTPANEL0
? And should that be_0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the first pass I matched the names with their C counterparts for consistency. I don't have a strong argument for this approach and have no problem diverging to improve readability.
nixnet/_enums.py
Outdated
NXTERM_PXI_STAR = _cconsts.NX_TERM_PXI_STAR | ||
NXTERM_PXI_CLK10 = _cconsts.NX_TERM_PXI_CLK10 | ||
NXTERM_10MHZTIMEBASE = _cconsts.NX_TERM_10_M_HZ_TIMEBASE | ||
NXTERM_1MHZTIMEBASE = _cconsts.NX_TERM_1_M_HZ_TIMEBASE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should be the word splitting on these?
1MHZ_TIMEBASE
1_MHZ_TIMEBASE
1_M_HZ_TIMEBASE
I lean towards: 1_MHZ_TIMEBASE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I second going with #_MHZ_TIMEBASE
.
nixnet/_enums.py
Outdated
NXTERM_MASTERTIMEBASE = _cconsts.NX_TERM_MASTER_TIMEBASE | ||
NXTERM_COMMTRIGGER = _cconsts.NX_TERM_COMM_TRIGGER | ||
NXTERM_STARTTRIGGER = _cconsts.NX_TERM_START_TRIGGER | ||
NXTERM_LOGTRIGGER = _cconsts.NX_TERM_LOG_TRIGGER |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come the words were combined (STARTTRIGGER
rather than START_TRIGGER
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both the XNET manual and the equivalent constant in the C API have the words joined as one. I will separate them for readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think where the confusion is coming in is definition of "word". In programming, there are several conventions for splitting words beyond " ".
- Capital letters (camelCase, PascalCase)
- underscores (snake_case, SCREAMING_CASE)
- dashes (arrow-case).
So we're switching from PascalCase to SCREAMING_CASE, so we replace the capital letter split with an underscore is the appropriate thing to do.
When I bootstrapped the API, I had a script automatically change the convention for us. It mostly worked (for example nxTerm_LogTrigger
was correctly mapped to NX_TERM_LOG_TRIGGER
). In some cases, it couldn't quite tell what the word boundaries are and got it very wrong (PDUs
-> PD_Us
-> pd_us
). Other cases require some more thought on our part (should MHz
be M_HZ
or MHZ
?).
For general reference, the constants look like
nxTerm_FlexRayStartCycle
nxTerm_PXI_Star
nxTerm_PXI_Trig4
The odd thing is that the C API is sometimes splitting with _
. In most cases, it is used to separate phrases ("NIXNET Terminal" from "FlexRay Start Cycle") but "PXI" and "Star" are part of the same phrase.
@@ -25,7 +25,7 @@ def main(): | |||
synchronized_input.intf.can_term = constants.CanTerm.ON | |||
|
|||
#Connect the start trigger terminals for the synchronized interface | |||
synchronized_input.connect_terminals(nixnet._cconsts.NX_TERM_FRONT_PANEL0, nixnet._cconsts.NX_TERM_START_TRIGGER) | |||
synchronized_input.connect_terminals(constants.Terminals.NX_TERM_FRONTPANEL0, constants.Terminals.NXTERM_STARTTRIGGER) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you need to modify these functions
- To convert from enum to numeric value
- To document the change in data type?
fix(examples): Added a Terminals enum and modified the synchronization example to use it Modified example to include feedback. Added enum names for terminals. Modified the connect terminal class to accept new terminal enums. Added unit tests Cleaned up files to pass Flake8 tests. Replaced .Next() function with for loop to pull out a single frame. Modified example to use contextlib2 and Lists Adding contextlib2 to testing dependencies Modifying print text to use for loop Adding contextlib2 dependency to requirements_mypy Added contextlib2 to setup.py to resolve jenkins test failures Added comma to seperate six and context lib requirements Removed tests for Generic Synchronization Removed imports for Generic Synchronization Extending line in documentation Added contextlib2 to travis.yml Created synchronization example with tests and documentation
a2cc128
to
b89e94f
Compare
|
||
|
||
class Terminal(enum.Enum): | ||
"Terminals to import/export from the XNET device" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use triple quotes for docstrings.
https://www.python.org/dev/peps/pep-0257/#one-line-docstrings
Interface properties need only be set once per interface. The connect_terminals function | ||
acts like an interface property and needs to be set only once per interface. Listening | ||
sessions can then be started with the "Session Only" scope. The last session to be started | ||
on a particul interface can be started with the normal scope which will cause it to start |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
particular*
Might be nice to actually link to docstrings for things like "connect_terminals", "SESSION_ONLY", and any other relevant properties.
If possible, it's a good idea to get input from techcomms on docstrings.
interface2 = 'CAN2' | ||
|
||
# Create the sessions. ExitStack() can be used for high session counts | ||
with nixnet.FrameInStreamSession(interface1) as interface1_input: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More of a suggestion here; would this naming be a little more descriptive?
input_interface = 'CAN1'
output_interface = 'CAN2'
with nixnet.FrameInStreamSession(input_interface) as input_session:
with nixnet.FrameOutStreamSession(output_interface) as output_session:
...
|
||
class Terminal(enum.Enum): | ||
"Terminals to import/export from the XNET device" | ||
FRONTPANEL_0 = _cconsts.NX_TERM_FRONT_PANEL0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should FRONTPANEL_0 and FRONTPANEL_1 be removed? They are duplicates of FRONT_PANEL_0 and FRONT_PANEL_1 below.
tox
successfully runs, including unit tests and style checks (see CONTRIBUTING.md).TODO: Check the above boxes with an 'x' to indicate what steps you have taken in preparing this Pull Request.
What does this Pull Request accomplish?
Proposes a generic synchronization example to demonstrate how to setup and use start triggers with multiple sessions on different interfaces.
Why should this Pull Request be merged?
Improve ease of use and getting started experience for new users.
What testing has been done?
Additional testing is required.