Skip to content
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

Refactoring, type annotations and fixes #51

Merged
merged 21 commits into from
May 10, 2024

Conversation

ngjunsiang
Copy link
Contributor

@ngjunsiang ngjunsiang commented Mar 11, 2024

This pull request covers a number of changes:

Use DataQueryDatumSpecification and DatumSpecification in composition

This implements the change proposed in #49 (comment)

Before:

class DataQueryDatumSpecification( object ):
"""List of fixed and variable datum records. Section 6.2.17 """
def __init__(self,
numberOfFixedDatums=0,
numberOfVariableDatums=0,
fixedDatumIDs=None,
variableDatumIDs=None):
""" Initializer for DataQueryDatumSpecification"""
self.numberOfFixedDatums = numberOfFixedDatums
""" Number of fixed datums"""
self.numberOfVariableDatums = numberOfVariableDatums
""" Number of variable datums"""
self.fixedDatumIDs = fixedDatumIDs or []
""" variable length list fixed datum IDs"""
self.variableDatumIDs = variableDatumIDs or []
""" variable length list variable datum IDs"""

After:

class DataQueryDatumSpecification(object):
"""Number and identification of fixed and variable datum records. Section 6.2.17"""
def __init__(self,
fixedDatumIDs=None,
variableDatumIDs=None):
"""Initializer for DataQueryDatumSpecification"""
self.fixedDatumIDs = fixedDatumIDs or []
"""variable length list fixed datum IDs"""
self.variableDatumIDs = variableDatumIDs or []
"""variable length list variable datum IDs"""
@property
def numberOfFixedDatumIDs(self) -> int:
return len(self.fixedDatumIDs)
@property
def numberOfVariableDatumIDs(self) -> int:
return len(self.variableDatumIDs)

Before:

class ActionRequestPdu( SimulationManagementFamilyPdu ):
"""Section 7.5.7. Request from simulation manager to a managed entity to perform a specified action. COMPLETE"""
def __init__(self,
requestID=0,
actionID=0,
numberOfFixedDatumRecords=0,
numberOfVariableDatumRecords=0,
fixedDatumRecords=None,
variableDatumRecordList=None):
""" Initializer for ActionRequestPdu"""
super(ActionRequestPdu, self).__init__()
self.requestID = requestID
""" identifies the request being made by the simulaton manager"""
self.actionID = actionID
""" identifies the particular action being requested(see Section 7 of SISO-REF-010)."""
self.numberOfFixedDatumRecords = numberOfFixedDatumRecords
""" Number of fixed datum records"""
self.numberOfVariableDatumRecords = numberOfVariableDatumRecords
""" Number of variable datum records"""
self.fixedDatumRecords = fixedDatumRecords or []
""" variable length list of fixed datums"""
self.variableDatumRecords = variableDatumRecords or []
""" variable length list of variable length datums"""
self.pduType = 16
""" initialize value """

After:

class ActionRequestPdu(SimulationManagementFamilyPdu):
"""Section 7.5.7. Request from simulation manager to a managed entity to perform a specified action. COMPLETE"""
def __init__(self,
requestID=0,
actionID=0,
fixedDatumRecords=None,
variableDatumRecords=None):
"""Initializer for ActionRequestPdu"""
super(ActionRequestPdu, self).__init__()
self.requestID = requestID
"""identifies the request being made by the simulaton manager"""
self.actionID = actionID
"""identifies the particular action being requested(see Section 7 of SISO-REF-010)."""
# Use DatumSpecification
self._datums = DatumSpecification(fixedDatumRecords or [],
variableDatumRecords or [])
self.pduType = 16
"""initialize value"""
@property
def numberOfFixedDatumRecords(self) -> int:
return self._datums.numberOfFixedDatumRecords
@property
def numberOfVariableDatumRecords(self) -> int:
return self._datums.numberOfVariableDatumRecords
@property
def fixedDatumRecords(self) -> list["FixedDatum"]:
return self._datums.fixedDatumRecords
@property
def variableDatumRecords(self) -> list["VariableDatum"]:
return self._datums.variableDatumRecords

Any fixes to DataQueryDatumSpecification and DatumSpecification will not need to be copied to the classes that use it.

Style fixes

  • removed inheritance from object which is a Python2 requirement; Python 3 no longer requires it
  • edited most class docstrings to follow PEP257 conventions
  • removed redundant comments and whitespace
  • moved shared attributes (pduType, protocolFamily) from init to class attribute.

Bugfixes

Some parsing code I found to be non-compliant with IEEE 1278.1-2012 has been fixed. Also replaced a few null() calls with calls to the appropriate classes (addressing #14, but not yet completely fixed)

Add type annotations

This implements the change proposed in #49 (comment)

Type aliases

# Type aliases (for readability)
enum8 = int
enum16 = int
enum32 = int
int16 = int
int32 = int
uint8 = int
uint16 = int
uint32 = int
uint64 = int
float32 = float
float64 = float
struct8 = bytes
struct16 = bytes
struct32 = bytes

The above type aliases are equivalent to the Python classes (int, float, bytes) assigned to them, and are used in type annotations to add more information about the required parameter type (enum, record (struct), signed or unsigned int, float) and size.

Type annotations have been added to most init parameters (some tricky ones are still unannotated), and it should be pretty clear from the annotations what is required now. In supported IDEs, these annotations can also be read and displayed in help text:
image

Future work

This is groundwork for future pull requests aimed at organising and structuring the codebase, so as to make it easier to address the open issues.

In particular, the current code structure and flow (instantiating a single PDU class to wrap an input/output stream for serialization/parsing) makes it tricky to handle bitfields and sub-enumerations. I'm working on a parallel effort, a code generator for enumerations from SISO-REF-010 which can be used with this package to provide introspection (auto-lookup of enum values) and unpack bitfields.

…PDUs

Instead of duplicating code already in the above two classes,
existing classes that implement the same functionality use them instead.

Access to the underlying attributes is provided through @property-decorated methods.

Affected classes:
Data Query PDU
Action Request PDU
Data PDU
Set Data PDU
Event Report PDU
Comment PDU
... and their Reliable counterparts
Remove unnecessary whitespace, and add any where required to follow PEP8 recommendations.
…ically

Instead of storing the number  of fixed datums and variable datums as an attribute,
calculate it from the list of fixed/variable datums when required.

The original attribute names are still accessible, through class properties.
This style is no longer required in Python 3; all classes that do not inherit another superclass
are automatically assumed to subclass object.
These are moved from the __init__() to class attributes.
This pattern makes it less likely for users to accidentally initialise the class with the
wrong recordType or recordLength, where these values are known beforehand.

Class attributes are accessible by instances of the class, so the serialize() and parse()
methods are unaffected.

### Further changes

- added type annotations to attributes, marking them as being an enum, int, or float,
  along with their size in bits
- fixed init types for some attributes
- removed some unnecessary comments
- added table UID in comments for some enums
These are calculated from the list of fixed/variable datum records instead.
The attributes are still accessible as properties.
Changes:
- removed redundant comments, whitespace
- added type aliases for Python int, float, bytes
- added type annotation for init parameters, padding
- added UID number in comments for enums from SISO-REF-010
- changed superclass attribute values (pduType, protocolFamily) from init to class attributes
- Fixed some parsing of arrays
@ngjunsiang
Copy link
Contributor Author

While this pull request is open, another quick question. I found this in the codebase:

class UnsignedDISInteger:
"""container class not in specification"""
def __init__(self, val=0):
self.val = val
"""unsigned integer"""
def serialize(self, outputStream):
"""serialize the class"""
outputStream.write_unsigned_int(self.val)
def parse(self, inputStream):
"""Parse a message. This may recursively call embedded objects."""
self.val = inputStream.read_unsigned_int()

This class appears to be unused. I have left it in the codebase for now. If it is also legacy code (in the same category as the *Chunk classes), let me know and I'll push another commit to remove it.

The type aliases are moved to their own file, types.py, to avoid a dependency loop.
This is considered safer as it automatically releases the file handle
if the program should crash before f.close() is reached.
@leif81
Copy link
Member

leif81 commented Mar 15, 2024

Thank-you @ngjunsiang
I'm going to be unavailable for the next week but I'll provide some feedback when I'm back

@ngjunsiang
Copy link
Contributor Author

quick ping @leif81 to see if there's anything else you need to see for this PR?

@leif81 leif81 requested review from leif81 and ricklentz April 23, 2024 03:37
@leif81
Copy link
Member

leif81 commented Apr 23, 2024

Have started to review, I should be able to finish up in next couple days.

Copy link
Member

@leif81 leif81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ngjunsiang the change is very large (>7000 lines changed), which makes it very difficult and time consuming to review. But I feel good about what I'm seeing. I will proceed with merging it. Thank-you for the improvement

@leif81 leif81 merged commit 5e67dfb into open-dis:master May 10, 2024
ngjunsiang added a commit to ngjunsiang/open-dis-python that referenced this pull request May 16, 2024
skrsta added a commit to skrsta/open-dis-python that referenced this pull request Jul 6, 2024
This fix work from open-dis#51 introduced in e9b6788 and regression from 98b2834
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants