diff --git a/docs/api_reference/bigendianstartbit12.gif b/docs/api_reference/bigendianstartbit12.gif new file mode 100644 index 0000000..fb25b49 Binary files /dev/null and b/docs/api_reference/bigendianstartbit12.gif differ diff --git a/docs/api_reference/database.rst b/docs/api_reference/database.rst index 652fe84..45a045b 100644 --- a/docs/api_reference/database.rst +++ b/docs/api_reference/database.rst @@ -9,7 +9,11 @@ nixnet.database database/database database/ecu database/frame + database/lin_sched + database/lin_sched_entry + database/pdu database/signal + database/subframe database/collection database/dbc_attributes database/dbc_signal_value_table diff --git a/docs/api_reference/database/frameoverviewsignalstartingbit12.gif b/docs/api_reference/database/frameoverviewsignalstartingbit12.gif new file mode 100644 index 0000000..5ce5cb2 Binary files /dev/null and b/docs/api_reference/database/frameoverviewsignalstartingbit12.gif differ diff --git a/docs/api_reference/database/lin_sched.rst b/docs/api_reference/database/lin_sched.rst new file mode 100644 index 0000000..a92e14a --- /dev/null +++ b/docs/api_reference/database/lin_sched.rst @@ -0,0 +1,7 @@ +nixnet.database.linsched +======================== + +.. automodule:: nixnet.database._lin_sched + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/api_reference/database/lin_sched_entry.rst b/docs/api_reference/database/lin_sched_entry.rst new file mode 100644 index 0000000..8fb2ecb --- /dev/null +++ b/docs/api_reference/database/lin_sched_entry.rst @@ -0,0 +1,7 @@ +nixnet.database.linsched_entry +============================== + +.. automodule:: nixnet.database._lin_sched_entry + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/api_reference/database/pdu.rst b/docs/api_reference/database/pdu.rst new file mode 100644 index 0000000..f0af6de --- /dev/null +++ b/docs/api_reference/database/pdu.rst @@ -0,0 +1,7 @@ +nixnet.database.pdu +=================== + +.. automodule:: nixnet.database._pdu + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/api_reference/database/pdusrequired.gif b/docs/api_reference/database/pdusrequired.gif new file mode 100644 index 0000000..3d17ad5 Binary files /dev/null and b/docs/api_reference/database/pdusrequired.gif differ diff --git a/docs/api_reference/database/subframe.rst b/docs/api_reference/database/subframe.rst new file mode 100644 index 0000000..9f78580 --- /dev/null +++ b/docs/api_reference/database/subframe.rst @@ -0,0 +1,7 @@ +nixnet.database.subframe +======================== + +.. automodule:: nixnet.database._subframe + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/api_reference/littleendianstartbit12.gif b/docs/api_reference/littleendianstartbit12.gif new file mode 100644 index 0000000..78fcf0f Binary files /dev/null and b/docs/api_reference/littleendianstartbit12.gif differ diff --git a/nixnet/_cconsts.py b/nixnet/_cconsts.py index c61ac6e..06018f7 100644 --- a/nixnet/_cconsts.py +++ b/nixnet/_cconsts.py @@ -403,7 +403,7 @@ NX_PROP_SESSION_INTF_SRC_TERM_START_TRIGGER = (0x00000090 | NX_CLASS_SESSION | NX_PRPTYPE_STRING) NX_PROP_SESSION_J1939_ADDRESS = (0x00000092 | NX_CLASS_SESSION | NX_PRPTYPE_U32) NX_PROP_SESSION_J1939_NAME = (0x00000094 | NX_CLASS_SESSION | NX_PRPTYPE_U64) -NX_PROP_SESSION_J1939ECU = (0x00000093 | NX_CLASS_SESSION | NX_PRPTYPE_REF) +NX_PROP_SESSION_J1939_ECU = (0x00000093 | NX_CLASS_SESSION | NX_PRPTYPE_REF) NX_PROP_SESSION_J1939_TIMEOUT_T1 = (0x00000095 | NX_CLASS_SESSION | NX_PRPTYPE_F64) NX_PROP_SESSION_J1939_TIMEOUT_T2 = (0x00000096 | NX_CLASS_SESSION | NX_PRPTYPE_F64) NX_PROP_SESSION_J1939_TIMEOUT_T3 = (0x00000097 | NX_CLASS_SESSION | NX_PRPTYPE_F64) @@ -416,7 +416,7 @@ NX_PROP_SESSION_J1939_MAX_REPEAT_CTS = (0x0000009E | NX_CLASS_SESSION | NX_PRPTYPE_U32) NX_PROP_SESSION_J1939_FILL_BYTE = (0x0000009F | NX_CLASS_SESSION | NX_PRPTYPE_U32) NX_PROP_SESSION_J1939_WRITE_QUEUE_SIZE = (0x000000A0 | NX_CLASS_SESSION | NX_PRPTYPE_U32) -NX_PROP_SESSION_J1939ECU_BUSY = (0x000000A1 | NX_CLASS_SESSION | NX_PRPTYPE_BOOL) +NX_PROP_SESSION_J1939_ECU_BUSY = (0x000000A1 | NX_CLASS_SESSION | NX_PRPTYPE_BOOL) NX_PROP_SESSION_INTF_CAN_EDGE_FILTER = (0x000000A2 | NX_CLASS_SESSION | NX_PRPTYPE_BOOL) NX_PROP_SESSION_INTF_CAN_TRANSMIT_PAUSE = (0x000000A3 | NX_CLASS_SESSION | NX_PRPTYPE_BOOL) NX_PROP_SESSION_INTF_CAN_DISABLE_PROT_EXCEPTION_HANDLING = (0x000000A4 | NX_CLASS_SESSION | NX_PRPTYPE_BOOL) @@ -477,7 +477,7 @@ NX_PROP_CLST_FRM_REFS = (0x00000004 | NX_CLASS_CLUSTER | NX_PRPTYPE_1_DREF) NX_PROP_CLST_NAME = (0x00000005 | NX_CLASS_CLUSTER | NX_PRPTYPE_STRING) NX_PROP_CLST_PDU_REFS = (0x00000008 | NX_CLASS_CLUSTER | NX_PRPTYPE_1_DREF) -NX_PROP_CLST_PD_US_REQD = (0x0000000A | NX_CLASS_CLUSTER | NX_PRPTYPE_BOOL) +NX_PROP_CLST_PDUS_REQD = (0x0000000A | NX_CLASS_CLUSTER | NX_PRPTYPE_BOOL) NX_PROP_CLST_PROTOCOL = (0x00000006 | NX_CLASS_CLUSTER | NX_PRPTYPE_U32) NX_PROP_CLST_SIG_REFS = (0x00000007 | NX_CLASS_CLUSTER | NX_PRPTYPE_1_DREF) NX_PROP_CLST_CAN_IO_MODE = (0x00000010 | NX_CLASS_CLUSTER | NX_PRPTYPE_U32) @@ -618,8 +618,8 @@ NX_PROP_ECU_LIN_CONFIG_NAD = (0x00000023 | NX_CLASS_ECU | NX_PRPTYPE_U32) NX_PROP_ECU_LIN_SUPPLIER_ID = (0x00000024 | NX_CLASS_ECU | NX_PRPTYPE_U32) NX_PROP_ECU_LIN_FUNCTION_ID = (0x00000025 | NX_CLASS_ECU | NX_PRPTYPE_U32) -NX_PROP_ECU_LINP_2MIN = (0x00000026 | NX_CLASS_ECU | NX_PRPTYPE_F64) -NX_PROP_ECU_LINS_TMIN = (0x00000027 | NX_CLASS_ECU | NX_PRPTYPE_F64) +NX_PROP_ECU_LIN_P2_MIN = (0x00000026 | NX_CLASS_ECU | NX_PRPTYPE_F64) +NX_PROP_ECU_LIN_ST_MIN = (0x00000027 | NX_CLASS_ECU | NX_PRPTYPE_F64) NX_PROP_ECU_J1939_PREFERRED_ADDRESS = (0x00000028 | NX_CLASS_ECU | NX_PRPTYPE_U32) NX_PROP_ECU_J1939_NODE_NAME = (0x00000029 | NX_CLASS_ECU | NX_PRPTYPE_U64) @@ -718,9 +718,9 @@ NX_CAN_LAST_ERR_BIT0 = 5 NX_CAN_LAST_ERR_CRC = 6 -NX_CA_NIO_MODE_CAN = 0 -NX_CA_NIO_MODE_CAN_FD = 1 -NX_CA_NIO_MODE_CAN_FD_BRS = 2 +NX_CAN_IO_MODE_CAN = 0 +NX_CAN_IO_MODE_CAN_FD = 1 +NX_CAN_IO_MODE_CAN_FD_BRS = 2 NX_FLEX_RAY_POC_STATE_DEFAULT_CONFIG = 0 NX_FLEX_RAY_POC_STATE_READY = 1 diff --git a/nixnet/_enums.py b/nixnet/_enums.py index 050f656..15b3752 100644 --- a/nixnet/_enums.py +++ b/nixnet/_enums.py @@ -1247,12 +1247,6 @@ class WriteState(enum.Enum): FLEX_RAY_SYMBOL = _cconsts.NX_STATE_FLEX_RAY_SYMBOL -class IntfCanFdIsoMode(enum.Enum): - ISO = _cconsts.NX_CAN_FD_MODE_ISO - NON_ISO = _cconsts.NX_CAN_FD_MODE_NON_ISO - ISO_LEGACY = _cconsts.NX_CAN_FD_MODE_ISO_LEGACY - - class CanFdIsoMode(enum.Enum): """CAN FD ISO MODE. @@ -1394,10 +1388,28 @@ class CanLastErr(enum.Enum): class CanIoMode(enum.Enum): - """CAN IO Mode.""" - CAN = _cconsts.NX_CA_NIO_MODE_CAN - CAN_FD = _cconsts.NX_CA_NIO_MODE_CAN_FD - CAN_FD_BRS = _cconsts.NX_CA_NIO_MODE_CAN_FD_BRS + """CAN I/O Mode. + + Values: + CAN: + This is the default CAN 2.0 A/B standard I/O mode as defined in ISO 11898-1:2003. + A fixed baud rate is used for transfer, + and the payload length is limited to 8 bytes. + CAN_FD: + This is the CAN FD mode as specified in the CAN with *Flexible Data-Rate specification*, + version 1.0. Payload lengths up to 64 are allowed, + but they are transmitted at a single fixed baud rate + (defined by :any:`Cluster.can_fd_baud_rate` or :any:`Interface.can_fd_baud_rate`). + CAN_FD_BRS: + This is the CAN FD as specified in the *CAN with Flexible Data-Rate* specification, + version 1.0, with the optional Baud Rate Switching enabled. + The same payload lengths as CAN FD mode are allowed; additionally, + the data portion of the CAN frame is transferred at a different (higher) baud rate + (defined by :any:`Cluster.can_fd_baud_rate` or :any:`Interface.can_fd_baud_rate`). + """ + CAN = _cconsts.NX_CAN_IO_MODE_CAN + CAN_FD = _cconsts.NX_CAN_IO_MODE_CAN_FD + CAN_FD_BRS = _cconsts.NX_CAN_IO_MODE_CAN_FD_BRS class FlexRayPocState(enum.Enum): @@ -1540,6 +1552,20 @@ class LinLastErr(enum.Enum): class LinProtocolVer(enum.Enum): + """LIN Protocol Version + + Values: + VER_1_2: + Version 1.2 + VER_1_3: + Version 1.3 + VER_2_0: + Version 2.0 + VER_2_1: + Version 2.1 + VER_2_2: + Version 2.2 + """ VER_1_2 = _cconsts.NX_LIN_PROTOCOL_VER_1_2 VER_1_3 = _cconsts.NX_LIN_PROTOCOL_VER_1_3 VER_2_0 = _cconsts.NX_LIN_PROTOCOL_VER_2_0 @@ -1561,6 +1587,24 @@ class GetDbcAttributeMode(enum.Enum): class Merge(enum.Enum): + """Cluster Merge Behavior + + Values: + COPY_USE_SOURCE: + The target object with all dependent child objects + is removed from the target cluster and replaced by the source objects. + COPY_USE_TARGET: + The source object is ignored (the target cluster object with child objects remains unchanged). + MERGE_USE_SOURCE: + This adds child objects from the source object to child objects from the destination object. + If target object contains a child object with the same name, + the child object from the source frame replaces it. + The source object properties (for example, payload length of the frame) replace the target properties. + MERGE_USE_TARGET: + This adds child objects from the source object to child objects from the destination object. + If the target object contains a child object with the same name, it remains unchanged. + The target object properties remain unchanged (for example, payload length). + """ COPY_USE_SOURCE = _cconsts.NXDB_MERGE_COPY_USE_SOURCE COPY_USE_TARGET = _cconsts.NXDB_MERGE_COPY_USE_TARGET MERGE_USE_SOURCE = _cconsts.NXDB_MERGE_MERGE_USE_SOURCE @@ -1715,7 +1759,18 @@ class CanTcvrCap(enum.Enum): class Protocol(enum.Enum): - """Protocol.""" + """Protocol. + + Values: + UNKNOWN: + Unknown protocol, + CAN: + CAN protocol. + FLEX_RAY: + FlexRay protocol. + LIN: + LIN protocol. + """ UNKNOWN = _cconsts.NX_PROTOCOL_UNKNOWN CAN = _cconsts.NX_PROTOCOL_CAN FLEX_RAY = _cconsts.NX_PROTOCOL_FLEX_RAY @@ -1723,7 +1778,19 @@ class Protocol(enum.Enum): class AppProtocol(enum.Enum): - """Application Protocol.""" + """Application Protocol. + + Values: + NONE: + The default application protocol. + J1939: + Indicates J1939 clusters. The value enables the following features: + + * Sending/receiving long frames as the SAE J1939 specification specifies, + using the J1939 transport protocol. + * Using a special notation for J1939 identifiers. + * Using J1939 address claiming. + """ NONE = _cconsts.NX_APP_PROTOCOL_NONE J1939 = _cconsts.NX_APP_PROTOCOL_J1939 @@ -1974,7 +2041,36 @@ class FrmFlexRayTiming(enum.Enum): EVENT = _cconsts.NX_FRM_FLEX_RAY_TIMING_EVENT -class FrmCanTiming(enum.Enum): +class CanFrameTiming(enum.Enum): + """CAN Frame Timing + + Values: + CYCLIC_DATA: + The transmitting ECU transmits the CAN data frame in a cyclic (periodic) manner. + The :any:`Frame.can_transmit_time` property defines the time between cycles. + The transmitting ECU ignores CAN remote frames received for this frame. + EVENT_DATA: + The transmitting ECU transmits the CAN data frame in an event-driven manner. + The :any:`Frame.can_transmit_time` property defines the minimum interval. + For NI-XNET, the event occurs when you write data to a session. + The transmitting ECU ignores CAN remote frames received for this frame. + CYCLIC_REMOTE: + The receiving ECU transmits the CAN remote frame in a cyclic (periodic) manner. + The :any:`Frame.can_transmit_time` property defines the time between cycles. + The transmitting ECU responds to each CAN remote frame by transmitting the associated CAN data frame. + EVENT_REMOTE: + The receiving ECU transmits the CAN remote frame in an event-driven manner. + The :any:`Frame.can_transmit_time` property defines the minimum interval. + For NI-XNET, the event occurs when you write a frame to a session. + The transmitting ECU responds to each CAN remote frame by transmitting the associated CAN data frame. + CYCLIC_EVENT: + This timing type is a combination of the cyclic and event timing. + The frame is transmitted when you write to a session, + but also periodically sending the last recent values written. + The :any:`Frame.can_transmit_time` property defines the cycle period. + There is no minimum interval time defined in this mode, + so be careful not to write too frequently to avoid creating a high busload. + """ CYCLIC_DATA = _cconsts.NX_FRM_CAN_TIMING_CYCLIC_DATA EVENT_DATA = _cconsts.NX_FRM_CAN_TIMING_EVENT_DATA CYCLIC_REMOTE = _cconsts.NX_FRM_CAN_TIMING_CYCLIC_REMOTE @@ -1982,31 +2078,115 @@ class FrmCanTiming(enum.Enum): CYCLIC_EVENT = _cconsts.NX_FRM_CAN_TIMING_CYCLIC_EVENT -class SigByteOrdr(enum.Enum): +class SigByteOrder(enum.Enum): + """Signal Byte Order + + Values: + Little Endian: + Higher significant signal bits are placed on higher byte addresses. + In NI-CAN, this was called Intel Byte Order. + + .. image:: littleendianstartbit12.gif + + **Little Endian Signal with Start Bit 12** + + Big Endian: + Higher significant signal bits are placed on lower byte addresses. + In NI-CAN, this was called Motorola Byte Order. + + .. image:: bigendianstartbit12.gif + + **Big Endian Signal with Start Bit 12** + """ LITTLE_ENDIAN = _cconsts.NX_SIG_BYTE_ORDR_LITTLE_ENDIAN BIG_ENDIAN = _cconsts.NX_SIG_BYTE_ORDR_BIG_ENDIAN class SigDataType(enum.Enum): + """Signal Data Type + + Values: + SIGNED: + Signed integer with positive and negative values. + UNSIGNED: + Unsigned integer with no negative values. + IEEE_FLOAT: + Float value with 7 or 15 significant decimal digits (32 bit or 64 bit). + """ SIGNED = _cconsts.NX_SIG_DATA_TYPE_SIGNED UNSIGNED = _cconsts.NX_SIG_DATA_TYPE_UNSIGNED IEEE_FLOAT = _cconsts.NX_SIG_DATA_TYPE_IEEE_FLOAT class LinSchedRunMode(enum.Enum): + """LIN Schedule Run Mode. + + Values: + CONTINUOUS: + The master runs the schedule continuously. + When the last entry executes, + the schedule starts again with the first entry. + ONCE: + The master runs the schedule once (all entries), + then returns to the previously running continuous schedule (or NULL). + If requests are submitted for multiple run-once schedules, + each run-once executes in succession based on its :any:`LinSched.priority`, + then the master returns to the continuous schedule (or NULL). + NULL: + All communication stops immediately. + A schedule with this run mode is called a *null schedule*. + """ CONTINUOUS = _cconsts.NX_LIN_SCHED_RUN_MODE_CONTINUOUS ONCE = _cconsts.NX_LIN_SCHED_RUN_MODE_ONCE NULL = _cconsts.NX_LIN_SCHED_RUN_MODE_NULL class LinSchedEntryType(enum.Enum): + """LIN Schedule Entry Type. + + Values: + UNCONDITIONAL: + A single frame transfers in this slot. + SPORADIC: + The master transmits in this slot. + The master can select from multiple frames to transmit. + Only updated frames are transmitted. + When more than one frame is updated, + the master decides by priority which frame to send. + The other updated frame remains pending + and can be sent when this schedule entry is processed the following time. + The order of unconditional frames in :any:`LinSchedEntry.frames` + (the first frame has the highest priority) determines the frame priority. + EVENT_TRIGGERED: + Multiple slaves can transmit an unconditional frame in this slot. + The slave transmits the frame only if at least one frame signal has been updated. + When a collision occurs (multiple slaves try to transmit in the same slot), + this is detected and resolved using a different schedule + specified in the :any:`LinSchedEntry.collision_resolving_schedule` property. + The resolving schedule runs once, + starting in the subsequent slot after the collision, + and automatically returns to the previous schedule + at the subsequent position where the collision occurred. + NODE_CONFIG_SERVICE: + The schedule entry contains a node configuration service. + The node configuration service is defined as raw data bytes + in :any:`LinSchedEntry.node_config_free_format_data_bytes`. + """ UNCONDITIONAL = _cconsts.NX_LIN_SCHED_ENTRY_TYPE_UNCONDITIONAL SPORADIC = _cconsts.NX_LIN_SCHED_ENTRY_TYPE_SPORADIC EVENT_TRIGGERED = _cconsts.NX_LIN_SCHED_ENTRY_TYPE_EVENT_TRIGGERED NODE_CONFIG_SERVICE = _cconsts.NX_LIN_SCHED_ENTRY_TYPE_NODE_CONFIG_SERVICE -class FrmLinChecksum(enum.Enum): +class LinFrameChecksum(enum.Enum): + """LIN Frame Transmitted Checksum + + Values: + CLASSIC: + Classic checksum. + ENHANCED: + Enhanced checksum. + """ CLASSIC = _cconsts.NX_FRM_LIN_CHECKSUM_CLASSIC ENHANCED = _cconsts.NX_FRM_LIN_CHECKSUM_ENHANCED diff --git a/nixnet/_props.py b/nixnet/_props.py index 2f5b87e..a1cc43b 100644 --- a/nixnet/_props.py +++ b/nixnet/_props.py @@ -1560,14 +1560,14 @@ def set_session_j1939_name( ) -def set_session_j1939ecu( +def set_session_j1939_ecu( ref, # type: int value, # type: int ): # type: (...) -> None _cprops.set_session_ref( ref, - _cconsts.NX_PROP_SESSION_J1939ECU, + _cconsts.NX_PROP_SESSION_J1939_ECU, value, ) @@ -1836,24 +1836,24 @@ def set_session_j1939_write_queue_size( ) -def get_session_j1939ecu_busy( +def get_session_j1939_ecu_busy( ref, # type: int ): # type: (...) -> bool return _cprops.get_session_bool( ref, - _cconsts.NX_PROP_SESSION_J1939ECU_BUSY, + _cconsts.NX_PROP_SESSION_J1939_ECU_BUSY, ) -def set_session_j1939ecu_busy( +def set_session_j1939_ecu_busy( ref, # type: int value, # type: bool ): # type: (...) -> None _cprops.set_session_bool( ref, - _cconsts.NX_PROP_SESSION_J1939ECU_BUSY, + _cconsts.NX_PROP_SESSION_J1939_ECU_BUSY, value, ) @@ -2504,13 +2504,13 @@ def get_cluster_pdu_refs( ) -def get_cluster_pd_us_reqd( +def get_cluster_pdus_reqd( ref, # type: int ): # type: (...) -> bool return _cprops.get_database_bool( ref, - _cconsts.NX_PROP_CLST_PD_US_REQD, + _cconsts.NX_PROP_CLST_PDUS_REQD, ) @@ -4968,46 +4968,46 @@ def set_ecu_lin_function_id( ) -def get_ecu_linp_2min( +def get_ecu_lin_p2_min( ref, # type: int ): # type: (...) -> float return _cprops.get_database_f64( ref, - _cconsts.NX_PROP_ECU_LINP_2MIN, + _cconsts.NX_PROP_ECU_LIN_P2_MIN, ) -def set_ecu_linp_2min( +def set_ecu_lin_p2_min( ref, # type: int value, # type: float ): # type: (...) -> None _cprops.set_database_f64( ref, - _cconsts.NX_PROP_ECU_LINP_2MIN, + _cconsts.NX_PROP_ECU_LIN_P2_MIN, value, ) -def get_ecu_lins_tmin( +def get_ecu_lin_st_min( ref, # type: int ): # type: (...) -> float return _cprops.get_database_f64( ref, - _cconsts.NX_PROP_ECU_LINS_TMIN, + _cconsts.NX_PROP_ECU_LIN_ST_MIN, ) -def set_ecu_lins_tmin( +def set_ecu_lin_st_min( ref, # type: int value, # type: float ): # type: (...) -> None _cprops.set_database_f64( ref, - _cconsts.NX_PROP_ECU_LINS_TMIN, + _cconsts.NX_PROP_ECU_LIN_ST_MIN, value, ) diff --git a/nixnet/_session/j1939.py b/nixnet/_session/j1939.py index 1966816..cae0df6 100644 --- a/nixnet/_session/j1939.py +++ b/nixnet/_session/j1939.py @@ -35,7 +35,7 @@ def name(self, value): _props.set_session_j1939_name(self._handle, value) def set_ecu(self, value): - _props.set_session_j1939ecu(self._handle, value) + _props.set_session_j1939_ecu(self._handle, value) @property def timeout_t1(self): @@ -135,8 +135,8 @@ def write_queue_size(self, value): @property def ecu_busy(self): - return _props.get_session_j1939ecu_busy(self._handle) + return _props.get_session_j1939_ecu_busy(self._handle) @ecu_busy.setter def ecu_busy(self, value): - _props.set_session_j1939ecu_busy(self._handle, value) + _props.set_session_j1939_ecu_busy(self._handle, value) diff --git a/nixnet/database/_cluster.py b/nixnet/database/_cluster.py index 63587b3..632f4e0 100644 --- a/nixnet/database/_cluster.py +++ b/nixnet/database/_cluster.py @@ -11,10 +11,7 @@ from nixnet.database import _collection from nixnet.database import _dbc_attributes -from nixnet.database import _ecu -from nixnet.database import _frame -from nixnet.database import _lin_sched -from nixnet.database import _pdu +from nixnet.database import _signal class Cluster(object): @@ -22,6 +19,10 @@ class Cluster(object): def __init__(self, handle): # type: (int) -> None + from nixnet.database import _ecu + from nixnet.database import _frame + from nixnet.database import _lin_sched + from nixnet.database import _pdu self._handle = handle self._dbc_attributes = None # type: typing.Optional[_dbc_attributes.DbcAttributeCollection] self._ecus = _collection.DbCollection( @@ -74,90 +75,293 @@ def merge( prefix, wait_for_complete): # type: (typing.Any, constants.Merge, typing.Text, bool) -> int + """Merges database objects and related subobjects from the source to this cluster. + + The source can be any of the following objects: + + * :any:`Frame<_frame.Frame>` + * :any:`Pdu` + * :any:`Ecu` + * :any:`LinSched` + * :any:`Cluster` + + All listed objects must have unique names in the cluster. + They are referenced here as objects, + as opposed to child objects (for example, a signal is a child of a frame). + + If the source object name is not used in the target cluster, + this function copies the source objects with the child objects to the target. + If an object with the same name exists in this cluster, + you can avoid name collisions by specifying the prefix to be added to the name. + + If an object with the same name exists in this cluster, + the merge behavior depends on the ``copy_mode`` input. + + **Example** + + Target frame F1(v1) has signals S1 and S2(v1). Source frame F1(v2) has signals S2(v2) and S3. + + (v1) and (v2) are two versions of one object with same name, but with different properties. + + * Result when ``copy_mode`` is ``COPY_USE_SOURCE``: F1(v2), S2(v2), S3. + * Result when ``copy_mode`` is ``COPY_USE_TARGET``: F1(v1), S1, S2(v1). + * Result when ``copy_mode`` is ``MERGE_USE_SOURCE``: F1(v2), S1, S2(v2), S3. + * Result when ``copy_mode`` is ``MERGE_USE_TARGET``: F1(v1), S1, S2(v1), S3. + + If the source object is a cluster, + this function copies all contained PDUs, ECUs, and LIN schedules + with their child objects to this cluster. + + Depending on the number of contained objects in the source and destination clusters, + the execution can take a longer time. + If ``wait_for_complete`` is ``True``, this function waits until the merging process gets completed. + If the execution completes without errors, + ``perecent_complete`` returns ``100``. + If ``wait_for_complete`` is ``False``, + the function returns quickly, + and ``perecent_complete`` returns values less than ``100``. + You must call :any:`Cluster.merge` repeatedly until ``perecent_complete`` returns ``100``. + You can use the time between calls to perform asynchronous tasks. + + Args: + source_obj(object): The object to be merged into this cluster. + copy_mode(:any:`Merge`): Defines the merging behavior if this cluster + already contains an object with the same name. + prefix(str): The prefix to be added to the source object name if an + object with the same name and type exists in this cluster. + wait_for_complete(bool): Determines whether the function returns directly + or waits until the entire transmission is completed. + Returns: + int: A value which indicates the merging progress as a percentage. ``100`` indicates completion. + """ return _funcs.nxdb_merge(self._handle, source_obj._handle, copy_mode.value, prefix, wait_for_complete) @property def baud_rate(self): + # type: (...) -> int + """int: Get or set the buad rate all custer nodes use. + + This baud rate represents the rate from the database, + so it is read-only from the session. + Use a session interface property (for example, :any:`Interface.baud_rate`) + to override the database baud rate with an application-specific baud rate. + + **CAN** + + For CAN, this rate can be 33333, 40000, 50000, 62500, 80000, 83333, + 100000, 125000, 160000, 200000, 250000, 400000, 500000, 800000, or + 1000000. Some transceivers may support only a subset of these values. + + **LIN** + + For LIN, this rate can be 2400-20000 inclusive. + + If you need values other than these, + use the custom settings as described in :any:`Interface.baud_rate`. + """ return _props.get_cluster_baud_rate64(self._handle) @baud_rate.setter def baud_rate(self, value): + # type: (int) -> None _props.set_cluster_baud_rate64(self._handle, value) @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the cluster object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_cluster_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_cluster_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the cluster object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured clusters in the database are not returned from + :any:`Database.clusters` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a cluster becomes invalid after the database has been opened, + the cluster still is returned from :any:`Database.clusters` even if + :any:`Database.show_invalid_from_open` to ``False``. + """ return _props.get_cluster_config_status(self._handle) @property - def database_ref(self): - return _props.get_cluster_database_ref(self._handle) + def database(self): + # type: () -> int + # todo: return a Database object here + handle = _props.get_cluster_database_ref(self._handle) + return handle @property def dbc_attributes(self): # type: () -> _dbc_attributes.DbcAttributeCollection - """:any:`nixnet.database._dbc_attributes.DbcAttributeCollection`: Access the cluster's DBC attributes.""" + """:any:`DbcAttributeCollection`: Access the cluster's DBC attributes.""" if self._dbc_attributes is None: self._dbc_attributes = _dbc_attributes.DbcAttributeCollection(self._handle) return self._dbc_attributes @property def ecus(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`Ecu` objects in this cluster. + + An ECU is assigned to a cluster when the ECU object is created. + You cannot change this assignment afterwards. + """ return self._ecus @property def frames(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`Frame<_frame.Frame>` objects in this cluster. + + A frame is assigned to a cluster when the frame object is created. + You cannot change this assignment afterwards. + """ return self._frames @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the cluster object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + If you use a FIBEX file, the short name comes from the file. + If you use a CANdb (.dbc), LDF (.ldf), + or NI-CAN (.ncd) file, + no cluster name is stored in the file, + so NI-XNET uses the name Cluster. + If you create the cluster yourself, + the name that you provide is used. + + A cluster name must be unique for all clusters in a database. + + This short name does not include qualifiers to ensure that it is unique, + such as the database name. It is for display purposes. + """ return _props.get_cluster_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_cluster_name(self._handle, value) @property def pdus(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`Pdu<_pdu.Pdu>` objects in this cluster. + + A PDU is assigned to a cluster when the PDU object is created. + You cannot change this assignment afterwards. + """ return self._pdus @property - def pd_us_reqd(self): - return _props.get_cluster_pd_us_reqd(self._handle) + def pdus_required(self): + # type: () -> bool + """bool: Returns whether using :any:`PDUs` in the database API is required for this cluster. + + If this property returns ``False``, + it is safe to use signals as child objects of a frame without PDUs. + This behavior is compatible with NI-XNET 1.1 or earlier. + Clusters from .dbc, .ncd, or FIBEX 2 files always return ``False`` for this property, + so using PDUs from those files is not required. + + If this property returns ``True``, + the cluster contains PDU configuration, + which requires reading the PDUs as frame child objects and then signals as PDU child objects, + as shown in the following figure. + + Internally, the database always uses PDUs, + but shows the same signal objects also as children of a frame. + + .. image:: pdusrequired.gif + + | + + For this property to return ``False``, + the following conditions must be fulfilled for all frames in the cluster: + + * Only one PDU is mapped to the frame. + * This PDU is not mapped to other frames. + * The PDU Start Bit in the frame is 0. + * The PDU Update Bit is not used. + + If the conditions are not fulfilled for a given frame, + signals from the frame are still returned, + but reading the property returns a warning. + """ + return _props.get_cluster_pdus_reqd(self._handle) @property def protocol(self): + # type: () -> constants.Protocol + """:any:`Protocol`: Get or set the cluster protocol.""" return constants.Protocol(_props.get_cluster_protocol(self._handle)) @protocol.setter def protocol(self, value): + # type: (constants.Protocol) -> None _props.set_cluster_protocol(self._handle, value.value) @property - def sig_refs(self): - return _props.get_cluster_sig_refs(self._handle) + def signals(self): + # type: () -> typing.Iterable[_signal.Signal] + """list of :any:`Signal<_signal.Signal>`: Returns a list of all :any:`Signal<_signal.Signal>` objects in this cluster.""" # NOQA: E501 + for handle in _props.get_cluster_sig_refs(self._handle): + yield _signal.Signal(handle) @property def can_io_mode(self): + # type: () -> constants.CanIoMode + """:any:`CanIoMode`: Get or set the CAN I/O Mode of the cluster.""" return constants.CanIoMode(_props.get_cluster_can_io_mode(self._handle)) @can_io_mode.setter def can_io_mode(self, value): + # type: (constants.CanIoMode) -> None _props.set_cluster_can_io_mode(self._handle, value.value) @property def can_fd_baud_rate(self): + # type: () -> int + """int: Get or set the fast data baud rate when :any:`Cluster.can_io_mode` is ``CanIoMode.CAN_FD_BRS``. + + Refer to the :any:`CanIoMode` for a description of ``CanIoMode.CAN_FD_BRS``. + Use a session interface property (for example, :any:`Interface.can_fd_baud_rate`) + to override the database fast baud rate with an application-specific fast baud rate. + + NI-XNET CAN hardware currently accepts the following numeric baud rates: + 200000, 250000, 400000, 500000, 800000, 1000000, 1250000, 1600000, + 2000000, 2500000, 4000000, 5000000, and 8000000. + Some transceivers may support only a subset of these values. + + If you need values other than these, + use the custom settings as described in :any:`Interface.can_fd_baud_rate`. + """ return _props.get_cluster_can_fd_baud_rate64(self._handle) @can_fd_baud_rate.setter def can_fd_baud_rate(self, value): + # type: (int) -> None _props.set_cluster_can_fd_baud_rate64(self._handle, value) @property @@ -430,14 +634,32 @@ def flex_ray_use_wakeup(self, value): @property def lin_schedules(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`LinSched` defined in this cluster. + + You assign a LIN schedule to a cluster when you create the LIN schedule object. + You cannot change this assignment afterwards. + The schedules in this collection are sorted alphabetically by schedule name. + """ return self._lin_sched @property def lin_tick(self): + # type: () -> float + """float: Returns the relative time between LIN ticks (relative f64 in seconds). + + The :any:`LinSchedEntry.delay` property must be a multiple of this tick. + + This tick is referred to as the "timebase" in the LIN specification. + + The :any:`Ecu.lin_master` property defines the Tick property in this cluster. + You cannot use the Tick property when there is no LIN Master property defined in this cluster. + """ return _props.get_cluster_lin_tick(self._handle) @lin_tick.setter def lin_tick(self, value): + # type: (float) -> None _props.set_cluster_lin_tick(self._handle, value) @property @@ -450,12 +672,21 @@ def flex_ray_alw_pass_act(self, value): @property def application_protocol(self): + # type: () -> constants.AppProtocol + """:any:`AppProtocol`: Get or set the application protocol.""" return constants.AppProtocol(_props.get_cluster_application_protocol(self._handle)) @application_protocol.setter def application_protocol(self, value): + # type: (constants.AppProtocol) -> None _props.set_cluster_application_protocol(self._handle, value.value) @property def can_fd_iso_mode(self): + # type: () -> constants.CanFdIsoMode + """:any:`CanFdIsoMode`: Returns the mode of a CAN FD cluster. + + The default is ``CanFdIsoMode.ISO``. + You define the value in a dialog box that appears when you define an alias for the database. + """ return constants.CanFdIsoMode(_props.get_cluster_can_fd_iso_mode(self._handle)) diff --git a/nixnet/database/_collection.py b/nixnet/database/_collection.py index 9e5158e..b547e11 100644 --- a/nixnet/database/_collection.py +++ b/nixnet/database/_collection.py @@ -13,7 +13,7 @@ class DbCollection(collections.Mapping): - """Collection of Database objects.""" + """Collection of database objects.""" def __init__(self, handle, db_type, prop_id, factory): # type: (int, constants.ObjectClass, int, typing.Any) -> None diff --git a/nixnet/database/_ecu.py b/nixnet/database/_ecu.py index a89df3d..040fd8b 100644 --- a/nixnet/database/_ecu.py +++ b/nixnet/database/_ecu.py @@ -6,7 +6,10 @@ from nixnet import _props from nixnet import constants + +from nixnet.database import _cluster from nixnet.database import _dbc_attributes +from nixnet.database import _frame class Ecu(object): @@ -37,52 +40,113 @@ def __repr__(self): return '{}(handle={})'.format(type(self).__name__, self._handle) @property - def clst_ref(self): - return _props.get_ecu_clst_ref(self._handle) + def cluster(self): + # type: () -> _cluster.Cluster + """:any:`Cluster`: Returns the parent cluster to which the ECU is connected. + + The parent cluster is determined when the ECU object is created. + You cannot change it afterwards. + """ + handle = _props.get_ecu_clst_ref(self._handle) + return _cluster.Cluster(handle) @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the ECU object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_ecu_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_ecu_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the ECU object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured ECUs in the database are not returned from + :any:`Cluster.ecus` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a ECU becomes invalid after opening the database, + the ECU still is returned from :any:`Cluster.ecus` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_ecu_config_status(self._handle) @property def dbc_attributes(self): # type: () -> _dbc_attributes.DbcAttributeCollection - """:any:`nixnet.database._dbc_attributes.DbcAttributeCollection`: Access the ECU's DBC attributes.""" + """:any:`DbcAttributeCollection`: Access the ECU's DBC attributes.""" if self._dbc_attributes is None: self._dbc_attributes = _dbc_attributes.DbcAttributeCollection(self._handle) return self._dbc_attributes @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the ECU object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + An ECU name must be unique for all ECUs in a cluster. + + This short name does not include qualifiers to ensure that it is unique, + such as the database and cluster name. + It is for display purposes. + """ return _props.get_ecu_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_ecu_name(self._handle, value) @property - def rx_frm_refs(self): - return _props.get_ecu_rx_frm_refs(self._handle) - - @rx_frm_refs.setter - def rx_frm_refs(self, value): - _props.set_ecu_rx_frm_refs(self._handle, value) + def frames_received(self): + # type: () -> typing.Iterable[_frame.Frame] + """list of :any:`Frame<_frame.Frame>`: Get or set a list of frames the ECU receives. + + This property defines all frames the ECU receives. + All frames an ECU receives in a given cluster must be defined in the same cluster. + """ + for ref in _props.get_ecu_rx_frm_refs(self._handle): + yield _frame.Frame(ref) + + @frames_received.setter + def frames_received(self, value): + # type: (typing.Iterable[_frame.Frame]) -> None + handle_list = [frame._handle for frame in value] + _props.set_ecu_rx_frm_refs(self._handle, handle_list) @property - def tx_frm_refs(self): - return _props.get_ecu_tx_frm_refs(self._handle) - - @tx_frm_refs.setter - def tx_frm_refs(self, value): - _props.set_ecu_tx_frm_refs(self._handle, value) + def frames_transmitted(self): + # type: () -> typing.Iterable[_frame.Frame] + """list of :any:`Frame<_frame.Frame>`: Get or set a list of frames the ECU transmits. + + This property defines all frames the ECU transmits. + All frames an ECU transmits in a given cluster must be defined in the same cluster. + """ + for ref in _props.get_ecu_tx_frm_refs(self._handle): + yield _frame.Frame(ref) + + @frames_transmitted.setter + def frames_transmitted(self, value): + # type: (typing.Iterable[_frame.Frame]) -> None + frame_handles = [frame._handle for frame in value] + _props.set_ecu_tx_frm_refs(self._handle, frame_handles) @property def flex_ray_is_coldstart(self): @@ -118,15 +182,19 @@ def flex_ray_connected_chs(self, value): @property def lin_master(self): + # type: () -> bool + """bool: Get or set whether the ECU is a LIN master (``True``) or LIN slave (``False``).""" return _props.get_ecu_lin_master(self._handle) @lin_master.setter def lin_master(self, value): + # type: (bool) -> None _props.set_ecu_lin_master(self._handle, value) @property def lin_protocol_ver(self): # type: () -> constants.LinProtocolVer + """:any:`LinProtocolVer`: Get or set the version of the LIN standard this ECU uses.""" return constants.LinProtocolVer(_props.get_ecu_lin_protocol_ver(self._handle)) @lin_protocol_ver.setter @@ -136,64 +204,138 @@ def lin_protocol_ver(self, value): @property def lin_initial_nad(self): + # type: () -> int + """int: Get or set the initial NAD of a LIN slave node. + + NAD is the address of a slave node and is used in diagnostic services. + Initial NAD is replaced by configured NAD with node configuration services. + + .. warning:: This property is not saved in the FIBEX database. + You can import it only from an LDF file. + """ return _props.get_ecu_lin_initial_nad(self._handle) @lin_initial_nad.setter def lin_initial_nad(self, value): + # type: (int) -> None _props.set_ecu_lin_initial_nad(self._handle, value) @property def lin_config_nad(self): + # type: () -> int + """int: Get or set the configured NAD of a LIN slave node. + + NAD is the address of a slave node and is used in diagnostic services. + Initial NAD is replaced by configured NAD with node configuration services. + + .. warning:: This property is not saved in the FIBEX database. + You can import it only from an LDF file. + """ return _props.get_ecu_lin_config_nad(self._handle) @lin_config_nad.setter def lin_config_nad(self, value): + # type: (int) -> None _props.set_ecu_lin_config_nad(self._handle, value) @property def lin_supplier_id(self): + # type: () -> int + """int: Get or set the supplier ID. + + Supplier ID is a 16-bit value identifying the supplier of the LIN node (ECU). + + .. warning:: This property is not saved in the FIBEX database. + You can import it only from an LDF file. + """ return _props.get_ecu_lin_supplier_id(self._handle) @lin_supplier_id.setter def lin_supplier_id(self, value): + # type: (int) -> None _props.set_ecu_lin_supplier_id(self._handle, value) @property def lin_function_id(self): + # type: () -> int + """int: Get or set the function ID. + + Function ID is a 16-bit value identifying the function of the LIN node (ECU). + + .. warning:: This property is not saved in the FIBEX database. + You can import it only from an LDF file. + """ return _props.get_ecu_lin_function_id(self._handle) @lin_function_id.setter def lin_function_id(self, value): + # type: (int) -> None _props.set_ecu_lin_function_id(self._handle, value) @property - def linp_2min(self): - return _props.get_ecu_linp_2min(self._handle) + def lin_p2_min(self): + # type: () -> float + """float: Get or set the minimum time in seconds between frame reception and node response. + + This is the minimum time between reception of the last frame + of the diagnostic request and the response sent by the node. - @linp_2min.setter - def linp_2min(self, value): - _props.set_ecu_linp_2min(self._handle, value) + .. warning:: This property is not saved in the FIBEX database. + You can import it only from an LDF file. + """ + return _props.get_ecu_lin_p2_min(self._handle) + + @lin_p2_min.setter + def lin_p2_min(self, value): + # type (float) -> None + _props.set_ecu_lin_p2_min(self._handle, value) @property - def lins_tmin(self): - return _props.get_ecu_lins_tmin(self._handle) + def lin_st_min(self): + # type: () -> float + """float: Get or set the minimum time in seconds for node preparation. + + This is the minimum time the node requires to prepare + for the next frame of the diagnostic service. - @lins_tmin.setter - def lins_tmin(self, value): - _props.set_ecu_lins_tmin(self._handle, value) + .. warning:: This property is not saved in the FIBEX database. + You can import it only from an LDF file. + """ + return _props.get_ecu_lin_st_min(self._handle) + + @lin_st_min.setter + def lin_st_min(self, value): + # type (float) -> None + _props.set_ecu_lin_st_min(self._handle, value) @property def j1939_preferred_address(self): + # type: () -> int + """int: Get or set the preferred J1939 node address to be used when simulating this ECU. + + If you assign this ECU to an XNET session (`j1939.set_ecu`), + XNET will start address claiming for this address using + :any:`Ecu.j1939_node_name` and use the address for the session when the address is granted. + """ return _props.get_ecu_j1939_preferred_address(self._handle) @j1939_preferred_address.setter def j1939_preferred_address(self, value): + # type: (int) -> None _props.set_ecu_j1939_preferred_address(self._handle, value) @property def j1939_node_name(self): + # type: () -> int + """int: Get or set the preferred J1939 node address to be used when simulating this ECU. + + If you assign this ECU to an XNET session (`j1939.set_ecu`), + XNET will start address claiming for this address using + this node name and :any:`Ecu.j1939_preferred_address`. + """ return _props.get_ecu_j1939_node_name(self._handle) @j1939_node_name.setter def j1939_node_name(self, value): + # type: (int) -> None _props.set_ecu_j1939_node_name(self._handle, value) diff --git a/nixnet/database/_frame.py b/nixnet/database/_frame.py index a9738d8..7c168f4 100644 --- a/nixnet/database/_frame.py +++ b/nixnet/database/_frame.py @@ -9,10 +9,10 @@ from nixnet import _props from nixnet import constants +from nixnet.database import _cluster from nixnet.database import _collection from nixnet.database import _dbc_attributes from nixnet.database import _signal -from nixnet.database import _subframe class Frame(object): @@ -20,6 +20,7 @@ class Frame(object): def __init__(self, handle): # type: (int) -> None + from nixnet.database import _subframe self._handle = handle self._dbc_attributes = None # type: typing.Optional[_dbc_attributes.DbcAttributeCollection] self._mux_static_signals = _collection.DbCollection( @@ -48,94 +49,353 @@ def __repr__(self): @property def application_protocol(self): - return _props.get_frame_application_protocol(self._handle) + # type: () -> constants.AppProtocol + """:any:`AppProtocol`: Get or set the frame's application protocol.""" + return constants.AppProtocol(_props.get_frame_application_protocol(self._handle)) @application_protocol.setter def application_protocol(self, value): - _props.set_frame_application_protocol(self._handle, value) + # type: (constants.AppProtocol) -> None + _props.set_frame_application_protocol(self._handle, value.value) @property - def cluster_ref(self): - return _props.get_frame_cluster_ref(self._handle) + def cluster(self): + # type: () -> _cluster.Cluster + """:any:`Cluster`: Get the parent cluster in which the frame has been created. + + You cannot change the parent cluster after the frame object has been created. + """ + handle = _props.get_frame_cluster_ref(self._handle) + return _cluster.Cluster(handle) @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the frame object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_frame_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_frame_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the frame object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured frames in the database are not returned from + :any:`Cluster.frames` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a frames becomes invalid after opening the database, + the frame still is returned from :any:`Cluster.frames` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_frame_config_status(self._handle) @property def default_payload(self): + # type: () -> typing.Iterable[int] + """list of int: Get or set the frame default payload, specified as a list of ints. + + Each int in the list represents a byte (U8). + The number of bytes in the list must match the :any:`Frame.payload_len` property. + + This property's initial value is an list of all ``0``, + except the frame is located in a CAN cluster with J1939 application protocol, + which uses ``0xFF`` by default. + For the database formats NI-XNET supports, + this property is not provided in the database file. + + When you use this frame within an NI-XNET session, + this property's use varies depending on the session mode. + The following sections describe this property's behavior for each session mode. + + Frame Output Single-Point and Frame Output Queued Modes: + Use this property when a frame transmits prior to a call to write. + This can occur when you set the :any:`SessionBase.auto_start` property to ``False`` + and start a session prior to writing. + When :any:`SessionBase.auto_start` is ``True`` (default), + the first frame write also starts frame transmit, so this property is not used. + + The following frame configurations potentially can transmit prior to a call to write: + + * :any:`Frame.can_timing_type` is ``CYCLIC_DATA``. + * :any:`Frame.can_timing_type` is ``CYCLIC_REMOTE``. + (for example, a remote frame received prior to a call to writing). + * :any:`Frame.can_timing_type` is ``EVENT_REMOTE``. + (for example, a remote frame received prior to a call to writing). + * :any:`Frame.can_timing_type` is ``CYCLIC_EVENT``. + * LIN frame in a schedule entry where :any:`LinSchedEntry.type` is ``UNCONDITIONAL``. + + The following frame configurations cannot transmit prior to writing, so this property is not used: + + * :any:`Frame.can_timing_type` is ``EVENT_DATA``.. + * LIN frame in a schedule entry where :any:`LinSchedEntry.type` is ``SPORADIC`` + or ``EVENT_TRIGGERED``. + + Frame Output Stream Mode: + This property is not used. Transmit is limited to frames provided to write. + + Signal Output Single-Point, Signal Output Waveform, and Signal Output XY Modes: + Use this property when a frame transmits prior to a call to write. + Refer to Frame Output Single-Point and Frame Output Queued Modes + for a list of applicable frame configurations. + + This property is used as the initial payload, + then each XNET Signal Default Value is mapped into that payload, + and the result is used for the frame transmit. + + Frame Input Stream and Frame Input Queued Modes: + This property is not used. + These modes do not return data prior to receiving frames. + + Frame Input Single-Point Mode: + This property is used for frames read returns prior to receiving the first frame. + + Signal Input Single-Point, Signal Input Waveform, and Signal Input XY Modes: + This property is not used. + Each :any:`Signal.default_value` is used when + reading from a session prior to receiving the first frame. + """ return _props.get_frame_default_payload(self._handle) @default_payload.setter def default_payload(self, value): + # type: (typing.List[int]) -> None _props.set_frame_default_payload(self._handle, value) @property def dbc_attributes(self): # type: () -> _dbc_attributes.DbcAttributeCollection - """:any:`nixnet.database._dbc_attributes.DbcAttributeCollection`: Access the frame's DBC attributes.""" + """:any:`DbcAttributeCollection`: Access the frame's DBC attributes.""" if self._dbc_attributes is None: self._dbc_attributes = _dbc_attributes.DbcAttributeCollection(self._handle) return self._dbc_attributes @property def id(self): + # type: () -> int + """int: Get or set the frame identifier. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this frame, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + + CAN: + For CAN frames, this is the Arbitration ID. + + When :any:`Frame.can_extended_id` is set to ``False``, + this is the standard CAN identifier with a size of 11 bits, + which results in allowed range of 0-2047. + However, the CAN standard disallows identifiers in which the first 7 bits are all recessive, + so the working range of identifiers is 0-2031. + + When :any:`Frame.can_extended_id` is set to ``True``, + this is the extended CAN identifier with a size of 29 bits, + which results in allowed range of 0-536870911. + LIN: + For LIN frames, this is the frame's ID (unprotected). + The valid range for a LIN frame ID is 0-63 (inclusive) + """ return _props.get_frame_id(self._handle) @id.setter def id(self, value): + # type: (int) -> None _props.set_frame_id(self._handle, value) @property def name(self): + # type: () -> typing.Text + """str: String identifying a frame object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A frame name must be unique for all frames in a cluster. + + This short name does not include qualifiers to ensure that it is unique, + such as the database and cluster name. + It is for display purposes. + """ return _props.get_frame_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_frame_name(self._handle, value) @property def payload_len(self): + # type: () -> int + """int: Get or set the number of bytes of data in the payload. + + For CAN and LIN, this is 0-8. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this frame, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_frame_payload_len(self._handle) @payload_len.setter def payload_len(self, value): + # type: (int) -> None _props.set_frame_payload_len(self._handle, value) @property - def sig_refs(self): - return _props.get_frame_sig_refs(self._handle) + def signals(self): + # type: () -> typing.Iterable[_signal.Signal] + """list of :any:`Signal<_signal.Signal>`:Get a list of all :any:`Signal<_signal.Signal>` objects in the frame. + + This property returns a list to all :any:`Signal<_signal.Signal>` objects in the frame, + including static and dynamic signals and the multiplexer signal. + """ + for handle in _props.get_frame_sig_refs(self._handle): + yield _signal.Signal(handle) @property - def can_ext_id(self): + def can_extended_id(self): + # type: () -> bool + """bool: Get or set whether the :any:`Frame.id` property in a CAN cluster is extended. + + The frame identifier represents a standard 11-bit (``False``) or extended 29-bit (``True``) arbitration ID. + """ return _props.get_frame_can_ext_id(self._handle) - @can_ext_id.setter - def can_ext_id(self, value): + @can_extended_id.setter + def can_extended_id(self, value): + # type: (bool) -> None _props.set_frame_can_ext_id(self._handle, value) @property def can_timing_type(self): - return constants.TimeType(_props.get_frame_can_timing_type(self._handle)) + # type: () -> constants.CanFrameTiming + """:any:`CanFrameTiming`: Get or set the CAN frame timing. + + Because this property specifies the behavior of the frame's transfer within the embedded system + (for example, a vehicle), + it describes the transfer between ECUs in the network. + In the following description, + transmitting ECU refers to the ECU that transmits the CAN data frame + (and possibly receives the associated CAN remote frame). + Receiving ECU refers to an ECU that receives the CAN data frame + (and possibly transmits the associated CAN remote frame). + + When you use the frame within an NI-XNET session, + an output session acts as the transmitting ECU, + and an input session acts as a receiving ECU. + For a description of how these CAN timing types apply to the NI-XNET session mode, + refer to `CAN Timing Type and Session Mode`. + + If you are using a FIBEX or AUTOSAR database, + this property is a required part of the XML schema for a frame, + so the default (initial) value is obtained from the file. + + If you are using a CANdb (.dbc) database, + this property is an optional attribute in the file. + If NI-XNET finds an attribute named GenMsgSendType, + that attribute is the default value of this property. + If the GenMsgSendType attribute begins with cyclic, + this property's default value is ``CYCLIC_DATA``; + otherwise, it is ``EVENT_DATA``. + If the CANdb file does not use the GenMsgSendType attribute, + this property uses a default value of ``EVENT_DATA``, + which you can change in your application. + + If you are using an .ncd database or an in-memory database, + this property uses a default value of ``EVENT_DATA``. + Within your application, + change this property to the desired timing type. + """ + return constants.CanFrameTiming(_props.get_frame_can_timing_type(self._handle)) @can_timing_type.setter def can_timing_type(self, value): + # type: (constants.CanFrameTiming) -> None _props.set_frame_can_timing_type(self._handle, value.value) @property - def can_tx_time(self): + def can_transmit_time(self): + # type: () -> float + """float: Get or set the time between consecutive frames from the transmitting ECU. + + The units are in seconds. + + Although the fractional part of the float can provide resolution of picoseconds, + the NI-XNET CAN transmit supports an accuracy of 500 microseconds. + Therefore, when used within an NI-XNET output session, + this property is rounded to the nearest 500 microsecond increment (0.0005). + + For a :any:`Frame.can_timing_type` of ``CYCLIC_DATA`` or ``CYCLIC_REMOTE``, + this property specifies the time between consecutive data/remote frames. + A time of 0.0 is invalid. + + For a :any:`Frame.can_timing_type` of ``EVENT_DATA`` or ``EVENT_REMOTE``, + this property specifies the minimum time between consecutive + data/remote frames when the event occurs quickly. + This is also known as the debounce time or minimum interval. + The time is measured from the end of previous frame (acknowledgment) to the start of the next frame. + A time of 0.0 specifies no minimum (back to back frames allowed). + + If you are using a FIBEX or AUTOSAR database, + this property is a required part of the XML schema for a frame, + so the default (initial) value is obtained from the file. + + If you are using a CANdb (.dbc) database, + this property is an optional attribute in the file. + If NI-XNET finds an attribute named GenMsgCycleTime, + that attribute is interpreted as a number of milliseconds and used as the default value of this property. + If the CANdb file does not use the GenMsgCycleTime attribute, + this property uses a default value of 0.1 (100 ms), + which you can change in your application. + + If you are using a .ncd database or an in-memory database, + this property uses a default value of 0.1 (100 ms). + Within your application, change this property to the desired time. + """ return _props.get_frame_can_tx_time(self._handle) - @can_tx_time.setter - def can_tx_time(self, value): + @can_transmit_time.setter + def can_transmit_time(self, value): + # type: (float) -> None _props.set_frame_can_tx_time(self._handle, value) @property @@ -216,64 +476,221 @@ def flex_ray_in_cyc_rep_ch_assigns(self, value): @property def lin_checksum(self): - return _props.get_frame_lin_checksum(self._handle) + # type: () -> constants.LinFrameChecksum + """:any:`LinFrameChecksum`: Returns whether the LIN frame transmitted checksum is classic or enhanced. + + The enhanced checksum considers the protected identifier when it is generated. + + The checksum is determined from the :any:`Ecu.lin_protocol_ver` properties + of the transmitting and receiving the frame. + The lower version of both ECUs is significant. + If the LIN version of both ECUs is 2.0 or higher, + the checksum type is enhanced; + otherwise, the checksum type is classic. + + Diagnostic frames (with decimal identifier 60 or 61) always use classic checksum, + even on LIN 2.x. + """ + return constants.LinFrameChecksum(_props.get_frame_lin_checksum(self._handle)) @property def mux_is_muxed(self): + # type: () -> bool + """bool: Returns whether this frame is data multiplexed. + + This property returns ``True`` if the frame contains a multiplexer signal. + Frames containing a multiplexer contain subframes that allow using bits + of the frame payload for different information (signals) depending on + the multiplexer value. + """ return _props.get_frame_mux_is_muxed(self._handle) @property - def mux_data_mux_sig_ref(self): + def mux_data_mux_signal(self): + # type: () -> _signal.Signal + """:any:`Signal<_signal.Signal>`: Returns a data multiplexer signal object in the frame. + + Use the :any:`Frame.mux_is_muxed` property to determine whether the frame contains a multiplexer signal. + + You can create a data multiplexer signal by creating a signal + and then setting the :any:`Signal.mux_is_data_mux` property to ``True``. + + A frame can contain only one data multiplexer signal. + + Raises: + XnetError: The data multiplexer signal is not defined in the frame + """ ref = _props.get_frame_mux_data_mux_sig_ref(self._handle) if ref == 0: # A bit of an abuse of errors _errors.check_for_error(_cconsts.NX_ERR_SIGNAL_NOT_FOUND) - return ref + return _signal.Signal(ref) @property def mux_static_signals(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of static :any:`Signal<_signal.Signal>` objects in this frame. + + Static signals are contained in every frame transmitted, + as opposed to dynamic signals, + which are transmitted depending on the multiplexer value. + + If the frame is not multiplexed, + this property returns the same objects as :any:`Frame.signals`. + """ return self._mux_static_signals @property def mux_subframes(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of :any:`SubFrame` objects in this frame. + + A subframe defines a group of signals transmitted using the same multiplexer value. + Only one subframe at a time is transmitted in the frame. + + A subframe is defined by creating a subframe object as a child of a frame. + """ return self._mux_subframes @property - def pdu_refs(self): - return _props.get_frame_pdu_refs(self._handle) + def pdus(self): + # actually returns typing.Iterable[_pdu.Pdu], but avoiding a circular import + # type: () -> typing.Iterable[typing.Any] + """list of :any:`Pdu`: Get or set a list that maps existing PDUs to a frame. + + A mapped PDU is transmitted inside the frame payload when the frame is transmitted. + You can map one or more PDUs to a frame and one PDU to multiple frames. + + Mapping PDUs to a frame requires setting three frame properties. + All three properties are lists of values: - @pdu_refs.setter - def pdu_refs(self, value): - _props.set_frame_pdu_refs(self._handle, value) + * :any:`Frame.pdus`: Set this property first to define + the sequence of values for the other two properties. + * :any:`Frame.pdu_start_bits`: Defines the start bit of the PDU inside the frame. + * :any:`Frame.pdu_update_bits`: Defines the update bit for the PDU inside the frame. + If the update bit is not used, set the value to ``-1``. + + Values on the same list position are corresponding. + For example, ``pdus[0]``, ``pdu_start_bits[0]``, + and ``pdu_update_bits[0]`` define the mapping for the first PDU in the frame. + + Databases imported from FIBEX prior to version 3.0, + from DBC, NCD, or LDF files have a strong one-to-one relationship between frames and PDUs. + Every frame has exactly one PDU mapped, and every PDU is mapped to exactly one frame. + + To unmap PDUs from a frame, set this property to an empty list. + A frame without mapped PDUs contains no signals. + + For CAN and LIN, NI-XNET supports only a one-to-one relationship between frames and PDUs. + For those interfaces, advanced PDU configuration returns + an error from the :any:`Frame.config_status` property and when creating a session. + If you do not use advanced PDU configuration, + you can avoid using PDUs in the database API + and create signals and subframes directly on a frame. + """ + from nixnet.database import _pdu + for handle in _props.get_frame_pdu_refs(self._handle): + yield _pdu.Pdu(handle) + + @pdus.setter + def pdus(self, value): + # value is actually typing.Iterable[_pdu.Pdu], but avoiding a circular import + # type: (typing.Iterable[typing.Any]) -> None + handle_list = [pdu._handle for pdu in value] + _props.set_frame_pdu_refs(self._handle, handle_list) @property def pdu_start_bits(self): + # type: () -> typing.Iterable[int] + """list of int: This property defines the start bits of PDUs mapped to a frame. + + A mapped PDU is transmitted inside the frame payload when the frame is transmitted. + You can map one or more PDUs to a frame and one PDU to multiple frames. + + Mapping PDUs to a frame requires setting of three frame properties. + All three properties are lists of values: + + * :any:`Frame.pdus`: Set this property first to define + the sequence of values for the other two properties. + * :any:`Frame.pdu_start_bits`: Defines the start bit of the PDU inside the frame. + * :any:`Frame.pdu_update_bits`: Defines the update bit for the PDU inside the frame. + If the update bit is not used, set the value to ``-1``. + + Values on the same list position are corresponding. + For example, ``pdus[0]``, ``pdu_start_bits[0]``, + and ``pdu_update_bits[0]`` define the mapping for the first PDU in the frame. + """ return _props.get_frame_pdu_start_bits(self._handle) @pdu_start_bits.setter def pdu_start_bits(self, value): + # type: (typing.List[int]) -> None _props.set_frame_pdu_start_bits(self._handle, value) @property def pdu_update_bits(self): + # type: () -> typing.Iterable[int] + """list of int: Get or set the update bits of PDUs mapped to a frame. + + If the update bit is not used for the PDU, set the value to -1. + The receiver uses the update bit to determine whether the frame sender has updated data in a particular PDU. + Update bits allow for the decoupling of a signal update from a frame occurrence. + Update bits is an optional PDU property. + + Mapping PDUs to a frame requires setting three frame properties. + All three properties are lists of values: + + * :any:`Frame.pdus`: Set this property first to define + the sequence of values for the other two properties. + * :any:`Frame.pdu_start_bits`: Defines the start bit of the PDU inside the frame. + * :any:`Frame.pdu_update_bits`: Defines the update bit for the PDU inside the frame. + If the update bit is not used, set the value to ``-1``. + + Values on the same list position are corresponding. + For example, ``pdus[0]``, ``pdu_start_bits[0]``, + and ``pdu_update_bits[0]`` define the mapping for the first PDU in the frame. + """ return _props.get_frame_pdu_update_bits(self._handle) @pdu_update_bits.setter def pdu_update_bits(self, value): + # type: (typing.List[int]) -> None _props.set_frame_pdu_update_bits(self._handle, value) @property def variable_payload(self): + # type: () -> bool + # This property is currently not documented in the C API. + # If/when we have C API documentation, we should add it here too. return _props.get_frame_variable_payload(self._handle) @variable_payload.setter def variable_payload(self, value): + # type: (bool) -> None _props.set_frame_variable_payload(self._handle, value) @property def can_io_mode(self): + # type: () -> constants.CanIoMode + """:any:`CanIoMode`: Get or set the frame's I/O mode. + + This property is used in ISO CAN FD+BRS mode only. + In this mode, + you can specify every frame to be transmitted in CAN 2.0, CAN FD, or CAN FD+BRS mode. + CAN FD+BRS frames require the interface to be in CAN FD+BRS mode; + otherwise, it is transmitted in CAN FD mode. + + When the interface is in Non-ISO CAN FD or Legacy ISO CAN FD mode, + this property is disregarded. + In Non-ISO CAN FD and Legacy ISO CAN FD mode, + you must use :any:`Interface.can_tx_io_mode` to switch the transmit mode. + + When the assigned database does not define the property in ISO CAN FD mode, + the frames are transmitted with :any:`Interface.can_io_mode`. + """ return constants.CanIoMode(_props.get_frame_can_io_mode(self._handle)) @can_io_mode.setter def can_io_mode(self, value): + # type: (constants.CanIoMode) -> None _props.set_frame_can_io_mode(self._handle, value.value) diff --git a/nixnet/database/_lin_sched.py b/nixnet/database/_lin_sched.py index 6f94703..2af3c94 100644 --- a/nixnet/database/_lin_sched.py +++ b/nixnet/database/_lin_sched.py @@ -8,20 +8,22 @@ from nixnet import _props from nixnet import constants +from nixnet.database import _cluster from nixnet.database import _collection -from nixnet.database import _lin_sched_entry class LinSched(object): + """Database LIN schedule""" def __init__(self, handle): # type: (int) -> None + from nixnet.database._lin_sched_entry import LinSchedEntry self._handle = handle self._entries = _collection.DbCollection( self._handle, constants.ObjectClass.LIN_SCHED_ENTRY, _cconsts.NX_PROP_LIN_SCHED_ENTRIES, - _lin_sched_entry.LinSchedEntry) + LinSchedEntry) def __eq__(self, other): if isinstance(other, self.__class__): @@ -43,45 +45,143 @@ def __repr__(self): return '{}(handle={})'.format(type(self).__name__, self._handle) @property - def clst_ref(self): - return _props.get_lin_sched_clst_ref(self._handle) + def cluster(self): + # type: () -> _cluster.Cluster + """:any:`Cluster`: Get the parent cluster in which the you created the schedule. + + You cannot change the parent cluster after creating the schedule object. + """ + handle = _props.get_lin_sched_clst_ref(self._handle) + cluster = _cluster.Cluster(handle) + return cluster @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the schedule object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_lin_sched_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_lin_sched_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the LIN schedule object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured schedules in the database are not returned from + :any:`Cluster.lin_schedules` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a schedule becomes invalid after opening the database, + the schedule still is returned from :any:`Cluster.lin_schedules` + even if :any:`Database.show_invalid_from_open` is ``False``. + + An example of invalid schedule configuration is when a required schedule property has not been defined. + For example, a schedule entry within this schedule has an undefined delay time. + """ return _props.get_lin_sched_config_status(self._handle) @property def entries(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of :any:`LinSchedEntry` for this LIN schedule. + + The position of each entry in this collection specifies the position in the schedule. + The database file and/or the order that you create entries at runtime determine the position. + """ return self._entries @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the LIN schedule object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), + and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A schedule name must be unique for all schedules in a cluster. + """ return _props.get_lin_sched_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_lin_sched_name(self._handle, value) @property def priority(self): + # type: () -> int + """int: Get or set the priority of a run-once LIN schedule. + + This priority applies when multiple run-once schedules are pending for execution. + + The valid range for this property is 1-254. + Lower values correspond to higher priority. + + This property applies only when the :any:`LinSched.run_mode` property is ``ONCE``. + Run-once schedule requests are queued for execution based on this property. + When all run-once schedules have completed, + the master returns to the previously running continuous schedule (or null). + + Run-continuous schedule requests are not queued. + Only the most recent run-continuous schedule is used, + and it executes only if no run-once schedule is pending. + Therefore, a run-continuous schedule has an effective priority of ``255``, + but this property is not used. + + Null schedule requests take effect immediately + and supercede any running run-once or run-continuous schedule. + The queue of pending run-once schedule requests + is flushed (emptied without running them). + Therefore, a null schedule has an effective priority of ``0``, + but this property is not used. + + This property is not read from the database, + but is handled like a database property. + After opening the database, the default value is returned, + and you can change the property. + But similar to database properties, + you cannot change it after a session is created. + """ return _props.get_lin_sched_priority(self._handle) @priority.setter def priority(self, value): + # type: (int) -> None _props.set_lin_sched_priority(self._handle, value) @property def run_mode(self): + # type: () -> constants.LinSchedRunMode + """:any:`LinSchedRunMode`: Get or set how the master runs this schedule. + + This property is not read from the database, + but is handled like a database property. + After opening the database, the default value is returned, + and you can change the property. + But similar to database properties, + you cannot change it after a session is created. + + Usually, the default value for the run mode is ``CONTINUOUS``. + If the schedule is configured to be a collision resolving table + for an event-triggered entry, the default is ``ONCE``. + """ return constants.LinSchedRunMode(_props.get_lin_sched_run_mode(self._handle)) @run_mode.setter def run_mode(self, value): + # type: (constants.LinSchedRunMode) -> None _props.set_lin_sched_run_mode(self._handle, value.value) diff --git a/nixnet/database/_lin_sched_entry.py b/nixnet/database/_lin_sched_entry.py index 0847192..77817aa 100644 --- a/nixnet/database/_lin_sched_entry.py +++ b/nixnet/database/_lin_sched_entry.py @@ -6,10 +6,13 @@ from nixnet import _props from nixnet import constants + from nixnet.database import _frame +from nixnet.database import _lin_sched class LinSchedEntry(object): + """Database LIN schedule entry""" def __init__(self, handle): # type: (int) -> None @@ -35,32 +38,78 @@ def __repr__(self): return '{}(handle={})'.format(type(self).__name__, self._handle) @property - def collision_res_sched(self): - return _props.get_lin_sched_entry_collision_res_sched(self._handle) - - @collision_res_sched.setter - def collision_res_sched(self, value): - _props.set_lin_sched_entry_collision_res_sched(self._handle, value) + def collision_resolving_schedule(self): + # type: (...) -> typing.Optional[_lin_sched.LinSched] + """:any:`LinSched`: Get or set a LIN schedule that resolves a collision for this event-triggered entry. + + This property applies only when :any:`LinSchedEntry.type` is ``EVENT_TRIGGERED``. + When a collision occurs for the event-triggered entry in this schedule, + the master must switch to the collision resolving schedule to transfer the unconditional frames successfully. + + When :any:`LinSchedEntry.type` is any value other than ``EVENT_TRIGGERED``, this property returns ``None``. + """ + handle = _props.get_lin_sched_entry_collision_res_sched(self._handle) + if not handle: + return None + lin_sched = _lin_sched.LinSched(handle) + return lin_sched + + @collision_resolving_schedule.setter + def collision_resolving_schedule(self, value): + # type: (_lin_sched.LinSched) -> None + _props.set_lin_sched_entry_collision_res_sched(self._handle, value._handle) @property def delay(self): + # type: () -> float + """float: Get or set the time from the start of this entry (slot) to the start of the next entry. + + The property uses a float value in seconds, with the fractional part used for milliseconds or microseconds. + """ return _props.get_lin_sched_entry_delay(self._handle) @delay.setter def delay(self, value): + # type: (float) -> None _props.set_lin_sched_entry_delay(self._handle, value) @property def event_id(self): + # type: () -> int + """int: Get or set the event-triggered entry identifier. + + This identifier is unprotected (NI-XNET handles the protection). + + This property applies only when :any:`LinSchedEntry.type` is ``EVENT_TRIGGERED``. + This identifier is for the event triggered entry itself, + and the first payload byte is for the protected identifier of the contained unconditional frame. + """ return _props.get_lin_sched_entry_event_id(self._handle) @event_id.setter def event_id(self, value): + # type: (int) -> None _props.set_lin_sched_entry_event_id(self._handle, value) @property def frames(self): # type: () -> typing.Iterable[_frame.Frame] + """list of :any:`Frame<_frame.Frame>`: Get or set a list of frames for this LIN schedule entry. + + If :any:`LinSchedEntry.type` is ``UNCONDITIONAL``, + this list contains one frame, + which is the single unconditional frame for this entry. + + If :any:`LinSchedEntry.type` is ``SPORADIC``, + this list contains one or more unconditional frames for this entry. + When multiple frames are pending for this entry, + the order in the list determines the priority to transmit. + + If :any:`LinSchedEntry.type` is ``EVENT_TRIGGERED``, + this list contains one or more unconditional frames for this entry. + When multiple frames for this entry are pending to be sent by distinct slaves, + this property uses the :any:`LinSchedEntry.collision_resolving_schedule` to process the frames. + """ for ref in _props.get_lin_sched_entry_frames(self._handle): yield _frame.Frame(ref) @@ -72,32 +121,89 @@ def frames(self, value): @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the LIN schedule entry object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A schedule entry name must be unique for all entries in the same schedule. + """ return _props.get_lin_sched_entry_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_lin_sched_entry_name(self._handle, value) @property def name_unique_to_cluster(self): + # type: () -> typing.Text + """str: Returns a LIN schedule entry name unique to the cluster that contains the object. + + If the single name is not unique within the cluster, + the name is .. + + You can pass the name to the `find` function to retrieve the reference to the object, + while the single name is not guaranteed success in `find` + because it may be not unique in the cluster. + """ return _props.get_lin_sched_entry_name_unique_to_cluster(self._handle) @property - def sched(self): - return _props.get_lin_sched_entry_sched(self._handle) + def schedule(self): + # type: () -> _lin_sched.LinSched + """:any:`LinSched`: Returns the LIN schedule that uses this entry. + + This LIN schedule is considered this entry's parent. + You define the parent schedule when you create the entry object. + You cannot change it afterwards. + """ + handle = _props.get_lin_sched_entry_sched(self._handle) + lin_sched = _lin_sched.LinSched(handle) + return lin_sched @property def type(self): + # type: () -> constants.LinSchedEntryType + """:any:`LinSchedEntryType`: Get or set the LIN schedule entry type. + + All frames that contain a payload are ``UNCONDITIONAL``. + The LIN schedule entry type determines the mechanism for transferring frames in this entry (slot). + """ return constants.LinSchedEntryType(_props.get_lin_sched_entry_type(self._handle)) @type.setter def type(self, value): + # type: (constants.LinSchedEntryType) -> None _props.set_lin_sched_entry_type(self._handle, value.value) @property - def nc_ff_data_bytes(self): + def node_config_free_format_data_bytes(self): + # type: (...) -> typing.Iterable[int] + """list of int: Get or set a list of 8 ints containing raw data for LIN node configuration. + + Node configuration defines a set of services used to configure slave nodes in the cluster. + Every service has a specific set of parameters coded in this int list. + In the LDF, file those parameters are stored, for example, in the node (ECU) or the frame object. + NI-XNET LDF reader composes those parameters to the byte values like they are sent on the bus. + The LIN specification document describes the node configuration services + and the mapping of the parameters to the free format bytes. + + The node configuration service is executed only if + :any:`LinSchedEntry.type` is set to ``NODE_CONFIG_SERVICE``. + + .. warning:: This property is not saved to the FIBEX file. + If you write this property, save the database, and reopen it, + the node configuration services are not contained in the database. + Writing this property is useful only in the NI-XNET session immediately following. + """ return _props.get_lin_sched_entry_nc_ff_data_bytes(self._handle) - @nc_ff_data_bytes.setter - def nc_ff_data_bytes(self, value): + @node_config_free_format_data_bytes.setter + def node_config_free_format_data_bytes(self, value): + # type: (typing.List[int]) -> None _props.set_lin_sched_entry_nc_ff_data_bytes(self._handle, value) diff --git a/nixnet/database/_pdu.py b/nixnet/database/_pdu.py index 67280c1..82fb6f7 100644 --- a/nixnet/database/_pdu.py +++ b/nixnet/database/_pdu.py @@ -2,19 +2,26 @@ from __future__ import division from __future__ import print_function +import typing # NOQA: F401 + from nixnet import _cconsts +from nixnet import _errors from nixnet import _props from nixnet import constants +from nixnet.database import _cluster from nixnet.database import _collection +from nixnet.database import _frame from nixnet.database import _signal -from nixnet.database import _subframe class Pdu(object): + """Database PDU""" def __init__(self, handle): # type: (int) -> None + from nixnet.database import _signal + from nixnet.database import _subframe self._handle = handle self._signals = _collection.DbCollection( self._handle, constants.ObjectClass.SIGNAL, _cconsts.NX_PROP_PDU_SIG_REFS, _signal.Signal) @@ -41,8 +48,14 @@ def __repr__(self): return '{}(handle={})'.format(type(self).__name__, self._handle) @property - def cluster_ref(self): - return _props.get_pdu_cluster_ref(self._handle) + def cluster(self): + # type: () -> _cluster.Cluster + """:any:`Cluster`: Get the parent cluster in which the PDU has been created. + + You cannot change the parent cluster after creating the PDU object. + """ + handle = _props.get_pdu_cluster_ref(self._handle) + return _cluster.Cluster(handle) @property def default_payload(self): @@ -54,52 +67,171 @@ def default_payload(self, value): @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the PDU object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_pdu_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_pdu_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the PDU object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured frames in the database are not returned from + :any:`Cluster.frames` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a frames becomes invalid after opening the database, + the frame still is returned from :any:`Cluster.frames` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_pdu_config_status(self._handle) @property - def frm_refs(self): - return _props.get_pdu_frm_refs(self._handle) + def frames(self): + # type: () -> typing.Iterable[_frame.Frame] + """list of :any:`Frame<_frame.Frame>`: Returns a list of all frames to which the PDU is mapped. + + A PDU is transmitted within the frames to which it is mapped. + + To map a PDU to a frame, + use the :any:`Frame.pdus`, + :any:`Frame.pdu_start_bits`, + and :any:`Frame.pdu_update_bits` properties. + You can map one PDU to multiple frames. + """ + for handle in _props.get_pdu_frm_refs(self._handle): + yield _frame.Frame(handle) @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the PDU object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A PDU name must be unique for all PDUs in a cluster. + """ return _props.get_pdu_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_pdu_name(self._handle, value) @property def payload_len(self): + # type: () -> int + """int: Get or set the size of the PDU data in bytes. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this PDU, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_pdu_payload_len(self._handle) @payload_len.setter def payload_len(self, value): + # type: (int) -> None _props.set_pdu_payload_len(self._handle, value) @property def signals(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of all :any:`Signal<_signal.Signal>` objects in this PDU. + + The collection includes all signals in the PDU, + including static and dynamic signals and the multiplexer signal. + """ return self._signals @property def mux_is_muxed(self): + # type: () -> bool + """bool: Returns ``True`` if the PDU contains a multiplexer signal. + + PDUs containing a multiplexer contain subframes that allow + using bits of the payload for different information (signals), + depending on the value of the :any:`SubFrame.mux_value` property. + """ return _props.get_pdu_mux_is_muxed(self._handle) @property - def mux_data_mux_sig_ref(self): - return _props.get_pdu_mux_data_mux_sig_ref(self._handle) + def mux_data_mux_signal(self): + # type: () -> _signal.Signal + """:any:`Signal<_signal.Signal>`: Data multiplexer signal in the PDU. + + This property returns the reference to the data multiplexer signal. + If data multiplexer is not defined in the PDU, the property raises an XnetError exception. + Use the :any:`Pdu.mux_is_muxed` property to determine whether the PDU contains a multiplexer signal. + + You can create a data multiplexer signal by creating a signal + and then setting the :any:`Signal.mux_is_data_mux` property to ``True``. + + A PDU can contain only one data multiplexer signal. + + Raises: + XnetError: The data multiplexer is not defined in the PDU. + """ + handle = _props.get_pdu_mux_data_mux_sig_ref(self._handle) + if handle == 0: + # A bit of an abuse of errors + _errors.check_for_error(_cconsts.NX_ERR_SIGNAL_NOT_FOUND) + return _signal.Signal(handle) @property - def mux_static_sig_refs(self): - return _props.get_pdu_mux_static_sig_refs(self._handle) + def mux_static_signals(self): + # type: () -> typing.Iterable[_signal.Signal] + """list of :any:`Signal<_signal.Signal>`: Returns a list of static signals in the PDU. + + Returns an list of signal objects in the PDU that do not depend + on value of the :any:`SubFrame.mux_value` property. + Static signals are contained in every PDU transmitted, + as opposed to dynamic signals, + which are transmitted depending on the value of the :any:`SubFrame.mux_value` property. + + You can create static signals by specifying the PDU as the parent object. + You can create dynamic signals by specifying a subframe as the parent. + + If the PDU is not multiplexed, + this property returns the same list as the :any:`Pdu.signals` property. + """ + for handle in _props.get_pdu_mux_static_sig_refs(self._handle): + yield _signal.Signal(handle) @property def mux_subframes(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of :any:`SubFrame` objects in this PDU. + + A subframe defines a group of signals transmitted using the same value of the :any:`SubFrame.mux_value`. + Only one subframe is transmitted in the PDU at a time. + """ return self._mux_subframes diff --git a/nixnet/database/_signal.py b/nixnet/database/_signal.py index fb68cc6..1308719 100644 --- a/nixnet/database/_signal.py +++ b/nixnet/database/_signal.py @@ -4,8 +4,11 @@ import typing # NOQA: F401 +from nixnet import _cconsts +from nixnet import _errors from nixnet import _props from nixnet import constants + from nixnet.database import _dbc_attributes from nixnet.database import _dbc_signal_value_table @@ -39,37 +42,101 @@ def __repr__(self): return '{}(handle={})'.format(type(self).__name__, self._handle) @property - def byte_ordr(self): - return constants.SigByteOrdr(_props.get_signal_byte_ordr(self._handle)) + def byte_order(self): + # type: () -> constants.SigByteOrder + """:any:`SigByteOrder`: Signal byte order in the frame payload. + + This property defines how signal bytes are ordered in the frame payload when the frame is loaded in memory. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this signal, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + * Set a value using the nxdbSetProperty function. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ + return constants.SigByteOrder(_props.get_signal_byte_ordr(self._handle)) - @byte_ordr.setter - def byte_ordr(self, value): + @byte_order.setter + def byte_order(self, value): + # type: (constants.SigByteOrder) -> None _props.set_signal_byte_ordr(self._handle, value.value) @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the signal object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_signal_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_signal_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the signal object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured signals in the database are not returned from + :any:`Frame.signals` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a signal becomes invalid after opening the database, + the signal still is returned from :any:`Frame.signals` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_signal_config_status(self._handle) @property def data_type(self): + # type: () -> constants.SigDataType + """:any:`SigDataType`: Get or set the signal data type. + + This property determines how the bits of a signal in a frame must be interpreted to build a value. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this signal, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return constants.SigDataType(_props.get_signal_data_type(self._handle)) @data_type.setter def data_type(self, value): + # type: (constants.SigDataType) -> None _props.set_signal_data_type(self._handle, value.value) @property def dbc_attributes(self): # type: () -> _dbc_attributes.DbcAttributeCollection - """:any:`nixnet.database._dbc_attributes.DbcAttributeCollection`: Access the signal's DBC attributes.""" + """:any:`DbcAttributeCollection`: Access the signal's DBC attributes.""" if self._dbc_attributes is None: self._dbc_attributes = _dbc_attributes.DbcAttributeCollection(self._handle) return self._dbc_attributes @@ -77,109 +144,348 @@ def dbc_attributes(self): @property def dbc_signal_value_table(self): # type: () -> _dbc_signal_value_table.DbcSignalValueTable - """:any:`nixnet.database._dbc_signal_value_table.DbcSignalValueTable`: Access the signal's DBC value table.""" + """:any:`DbcSignalValueTable`: Access the signal's DBC value table.""" return self._dbc_signal_value_table @property - def default(self): + def default_value(self): + # type: () -> float + """float: Get or set the signal default value, specified as scaled floating-point units. + + The initial value of this property comes from the database. + If the database does not provide a value, this property uses a default value of 0.0. + + For all three signal output sessions, + this property is used when a frame transmits prior to writing to a session. + The :any:`Frame.default_payload` property is used as the initial payload, + then the default value of each signal is mapped into that payload using this property, + and the result is used for the frame transmit. + + For all three signal input sessions, + this property is returned for each signal when reading a session prior to receiving the first frame. + + For more information about when this property is used, + refer to the discussion of read and write for each session mode. + """ return _props.get_signal_default(self._handle) - @default.setter - def default(self, value): + @default_value.setter + def default_value(self, value): + # type: (float) -> None _props.set_signal_default(self._handle, value) @property - def frame_ref(self): - return _props.get_signal_frame_ref(self._handle) + def frame(self): + # actually returns _frame.Frame, but avoiding a circular import + # type: () -> typing.Any + """:any:`Frame<_frame.Frame>`: Returns the signal parent frame object. + + The parent frame is defined when the signal object is created. You cannot change it afterwards. + """ + from nixnet.database import _frame + ref = _props.get_signal_frame_ref(self._handle) + return _frame.Frame(ref) @property def max(self): + # type: () -> float + """float: Get or set the scaled signal value maximum. + + Session read and write methods do not limit the signal value to a maximum value. + Use this database property to set the maximum value. + """ return _props.get_signal_max(self._handle) @max.setter def max(self, value): + # type: (float) -> None _props.set_signal_max(self._handle, value) @property def min(self): + # type: () -> float + """float: The scaled signal value minimum. + + Session read and write methods do not limit the signal value to a minimum value. + Use this database property to set the minimum value. + """ return _props.get_signal_min(self._handle) @min.setter def min(self, value): + # type: (float) -> None _props.set_signal_min(self._handle, value) @property def name(self): + # type: () -> typing.Text + """str: Get or set a string identifying a signal object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A signal name must be unique for all signals in a frame. + + This short name does not include qualifiers to ensure that it is unique, + such as the database, cluster, and frame name. + It is for display purposes. + """ return _props.get_signal_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_signal_name(self._handle, value) @property def name_unique_to_cluster(self): + # type: () -> typing.Text + """str: Returns a signal name unique to the cluster that contains the signal. + + If the single name is not unique within the cluster, + the name is .. + + You can pass the name to the `find` function to retrieve the reference to the object, + while the single name is not guaranteed success in `find` because it may be not unique in the cluster. + """ return _props.get_signal_name_unique_to_cluster(self._handle) @property def num_bits(self): + # type: () -> int + """int: The number of bits the signal uses in the frame payload. + + IEEE Float numbers are limited to 32 bit or 64 bit. + + Integer (signed and unsigned) numbers are limited to 1-52 bits. + NI-XNET converts all integers to doubles (64-bit IEEE Float). + Integer numbers with more than 52 bits + (the size of the mantissa in a 64-bit IEEE Float) + cannot be converted exactly to double, and vice versa; therefore, + NI-XNET does not support this. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this signal, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_signal_num_bits(self._handle) @num_bits.setter def num_bits(self, value): + # type: (int) -> None _props.set_signal_num_bits(self._handle, value) @property - def pdu_ref(self): - return _props.get_signal_pdu_ref(self._handle) + def pdu(self): + # actually returns _pdu.Pdu, but avoiding a circular import + # type: () -> typing.Any + """:any:`Pdu`: Returns to the signal's parent PDU. + + The parent PDU is defined when the signal object is created. + You cannot change it afterwards. + """ + from nixnet.database import _pdu + ref = _props.get_signal_pdu_ref(self._handle) + return _pdu.Pdu(ref) @property - def scale_fac(self): + def scale_factor(self): + # type: () -> float + """float: Get or set factor `a` for linear scaling `ax+b`. + + Linear scaling is applied to all signals with the IEEE Float data type, + unsigned and signed. + For identical scaling 1.0x+0.0, + NI-XNET optimized scaling routines do not perform the multiplication and addition + """ return _props.get_signal_scale_fac(self._handle) - @scale_fac.setter - def scale_fac(self, value): + @scale_factor.setter + def scale_factor(self, value): + # type: (float) -> None _props.set_signal_scale_fac(self._handle, value) @property - def scale_off(self): + def scale_offset(self): + # type: () -> float + """float: Get or set offset `b` for linear scaling `ax+b`. + + Linear scaling is applied to all signals with the IEEE Float data type, + unsigned and signed. + For identical scaling 1.0x+0.0, + NI-XNET optimized scaling routines do not perform the multiplication and addition + """ return _props.get_signal_scale_off(self._handle) - @scale_off.setter - def scale_off(self, value): + @scale_offset.setter + def scale_offset(self, value): + # type: (float) -> None _props.set_signal_scale_off(self._handle, value) @property def start_bit(self): + """int: Get or set the least significant signal bit position in the frame payload. + + This property determines the signal starting point in the frame. + For the integer data type (signed and unsigned), + it means the binary signal representation least significant bit position. + For IEEE Float signals, it means the mantissa least significant bit. + + The NI-XNET Database Editor shows a graphical overview of the frame. + It enumerates the frame bytes on the left and the byte bits on top. + The bit number in the frame is calculated as byte number x 8 + bit number. + The maximum bit number in a CAN or LIN frame is 63 (7 x 8 + 7); + the maximum bit number in a FlexRay frame is 2031 (253 x 8 + 7). + + .. image:: frameoverviewsignalstartingbit12.gif + + **Frame Overview in the NI-XNET Database Editor with a Signal Starting in Bit 12** + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this signal, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_signal_start_bit(self._handle) @start_bit.setter def start_bit(self, value): + # type: (typing.Any) -> typing.Any _props.set_signal_start_bit(self._handle, value) @property def unit(self): + # type: () -> typing.Text + """str: Get or set the signal value unit. + + NI-XNET does not use the unit internally for calculations. + You can use the string to display the signal value along with the unit. + """ return _props.get_signal_unit(self._handle) @unit.setter def unit(self, value): + # type: (typing.Text) -> None _props.set_signal_unit(self._handle, value) @property def mux_is_data_mux(self): + # type: () -> bool + """bool: Get or set whether this signal is a multiplexer signal. + + A frame containing a multiplexer value is called a multiplexed frame. + + A multiplexer defines an area within the frame to contain different information + (dynamic signals) depending on the multiplexer signal value. + Dynamic signals with a different multiplexer value + (defined in a different subframe) + can share bits in the frame payload. + The multiplexer signal value determines which dynamic signals are transmitted in the given frame. + + To define dynamic signals in the frame transmitted with a given multiplexer value, + you first must create a subframe in this frame and set the multiplexer value in the subframe. + Then you must create dynamic signals using + :any:`SubFrame.dynamic_signals` to create child signals of this subframe. + + Multiplexer signals may not overlap other static or dynamic signals in the frame. + + Dynamic signals may overlap other dynamic signals when they have a different multiplexer value. + + A frame may contain only one multiplexer signal. + + The multiplexer signal is not scaled. + Scaling factor and offset do not apply. + + In NI-CAN, the multiplexer signal was called mode channel. + """ return _props.get_signal_mux_is_data_mux(self._handle) @mux_is_data_mux.setter def mux_is_data_mux(self, value): + # type: (bool) -> None _props.set_signal_mux_is_data_mux(self._handle, value) @property def mux_is_dynamic(self): + # type: () -> bool + """bool: returns whether this signal is a dynamic signal. + + Use this property to determine if a signal is static or dynamic. + Dynamic signals are transmitted in the frame when the multiplexer signal + in the frame has a given value specified in the subframe. + Use the :any:`Signal.mux_value` property to determine with which + multiplexer value the dynamic signal is transmitted. + + This property is read only. + To create a dynamic signal, + create the signal object as a child of a subframe instead of a frame. + The dynamic signal cannot be changed to a static signal afterwards. + + In NI-CAN, dynamic signals were called mode-dependent signals. + """ return _props.get_signal_mux_is_dynamic(self._handle) @property def mux_value(self): + # type: () -> int + """int: Returns the multiplexer value of a dynamic signal. + + The multiplexer value applies to dynamic signals only + (when :any:`Signal.mux_is_dynamic` is ``True``). + This property defines which multiplexer value is transmitted in the + multiplexer signal when this dynamic signal is transmitted in the frame. + + The multiplexer value is determined in the subframe. + All dynamic signals that are children of the same subframe object use the same multiplexer value. + + Dynamic signals with the same multiplexer value may not overlap each other, + the multiplexer signal, or static signals. + """ return _props.get_signal_mux_value(self._handle) @property - def mux_subfrm_ref(self): - return _props.get_signal_mux_subfrm_ref(self._handle) + def mux_subframe(self): + # actually returns _subframe.SubFrame, but avoiding a circular import + # type: () -> typing.Any + """:any:`SubFrame`: Returns the subframe parent. + + This property is valid only for dynamic signals that have a subframe parent. + For static signals or the multiplexer signal, + this property raises an XnetError exception. + + Raises: + XnetError: The signal does not have a subframe parent. + """ + from nixnet.database import _subframe + ref = _props.get_signal_mux_subfrm_ref(self._handle) + if ref == 0: + # A bit of an abuse of errors + _errors.check_for_error(_cconsts.NX_ERR_FRAME_NOT_FOUND) + return _subframe.SubFrame(ref) diff --git a/nixnet/database/_subframe.py b/nixnet/database/_subframe.py index 301f8b7..cabf9c8 100644 --- a/nixnet/database/_subframe.py +++ b/nixnet/database/_subframe.py @@ -2,18 +2,22 @@ from __future__ import division from __future__ import print_function +import typing # NOQA: F401 + from nixnet import _cconsts from nixnet import _props from nixnet import constants from nixnet.database import _collection -from nixnet.database import _signal +from nixnet.database import _frame class SubFrame(object): + """Database subframe""" def __init__(self, handle): # type: (int) -> None + from nixnet.database import _signal self._handle = handle self._dyn_signals = _collection.DbCollection( self._handle, constants.ObjectClass.SIGNAL, _cconsts.NX_PROP_SUBFRM_DYN_SIG_REFS, _signal.Signal) @@ -39,36 +43,124 @@ def __repr__(self): @property def config_status(self): + # type: () -> int + """int: Returns the subframe object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured subframes in the database are not returned from + :any:`Frame.mux_subframes` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a subframe becomes invalid after opening the database, + the subframe still is returned from :any:`Frame.mux_subframes` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_subframe_config_status(self._handle) @property - def dyn_signals(self): + def dynamic_signals(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of dynamic :any:`Signal<_signal.Signal>` objects in the subframe. + + Those signals are transmitted when the multiplexer signal + in the frame has the multiplexer value defined in the subframe. + """ return self._dyn_signals @property - def frm_ref(self): - return _props.get_subframe_frm_ref(self._handle) + def frame(self): + # type: () -> _frame.Frame + """:any:`Frame<_frame.Frame>`: Returns the reference to the parent frame. + + The parent frame is defined when the subframe is created, + and you cannot change it afterwards. + """ + handle = _props.get_subframe_frm_ref(self._handle) + return _frame.Frame(handle) @property def mux_value(self): + # type: () -> int + """int: Get or set the multiplexer value for this subframe. + + This property specifies the multiplexer signal value used when the + dynamic signals in this subframe are transmitted in the frame. + Only one subframe is transmitted at a time in the frame. + + There also is a multiplexer value for a signal object as a read-only property. + It reflects the value set on the parent subframe object. + + This property is required. If the property does not contain a valid value, + and you create an XNET session that uses this subframe, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_subframe_mux_value(self._handle) @mux_value.setter def mux_value(self, value): + # type: (int) -> None _props.set_subframe_mux_value(self._handle, value) @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the subframe object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A subframe name must be unique for all subframes in a frame. + + This short name does not include qualifiers to ensure that it is unique, + such as the database, cluster, and frame name. It is for display purposes. + """ return _props.get_subframe_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_subframe_name(self._handle, value) @property - def pdu_ref(self): - return _props.get_subframe_pdu_ref(self._handle) + def pdu(self): + # actually returns _pdu.Pdu, but avoiding a circular import + # type: () -> typing.Any + """:any:`Pdu`: Returns the subframe's parent PDU. + + This property returns the reference to the subframe's parent PDU. + The parent PDU is defined when the subframe object is created. + You cannot change it afterwards. + """ + from nixnet.database import _pdu + handle = _props.get_subframe_pdu_ref(self._handle) + return _pdu.Pdu(handle) @property def name_unique_to_cluster(self): + # type: () -> typing.Text + """str: Returns a subframe name unique to the cluster that contains the subframe. + + If the single name is not unique within the cluster, the name is .. + + You can pass the name to the `find` function to retrieve the reference to the object, + while the single name is not guaranteed success in `find` + because it may be not unique in the cluster. + """ return _props.get_subframe_name_unique_to_cluster(self._handle) diff --git a/nixnet/database/database.py b/nixnet/database/database.py index a33fdc2..0277bad 100644 --- a/nixnet/database/database.py +++ b/nixnet/database/database.py @@ -11,7 +11,6 @@ from nixnet import constants from nixnet import errors -from nixnet.database import _cluster from nixnet.database import _collection @@ -32,6 +31,8 @@ def __init__(self, database_name): # type: (typing.Text) -> None self._handle = None # To satisfy `__del__` in case nxdb_open_database throws self._handle = _funcs.nxdb_open_database(database_name) + + from nixnet.database import _cluster self._clusters = _collection.DbCollection( self._handle, constants.ObjectClass.CLUSTER, _cconsts.NX_PROP_DATABASE_CLST_REFS, _cluster.Cluster) @@ -101,12 +102,11 @@ def close(self, close_all_refs=False): """ if self._handle is None: warnings.warn( - 'Attempting to close NI-XNET system but system was already ' + 'Attempting to close NI-XNET database but database was already ' 'closed', errors.XnetResourceWarning) return _funcs.nxdb_close_database(self._handle, close_all_refs) - self._handle = None def save(self, db_filepath=""): @@ -160,6 +160,39 @@ def clusters(self): @property def show_invalid_from_open(self): # type: () -> bool + """bool: Show or hide :any:`Frame<_frame.Frame>` and :any:`Signal<_signal.Signal>` objects that are invalid. + + After opening a database, this property always is set to ``False``, + meaning that invalid :any:`Cluster`, :any:`Frame<_frame.Frame>`, + and :any:`Signal<_signal.Signal>` objects + are not returned in properties that return a :any:`DbCollection` for the database + (for example, :any:`Cluster.frames` and :any:`Frame.mux_static_signals`). + Invalid :any:`Cluster`, :any:`Frame<_frame.Frame>`, + and :any:`Signal<_signal.Signal>` objects are incorrectly defined + and therefore cannot be used in the bus communication. + The ``False`` setting is recommended when you use the database to create XNET sessions. + + In case the database was opened to correct invalid configuration + (for example, in a database editor), + you must set the property to ``True`` prior to reading properties that return + a :any:`DbCollection` for the database + (for example, :any:`Cluster.frames` and :any:`Frame.mux_static_signals`). + + For invalid objects, + the :any:`Cluster.config_status`, + :any:`Frame.config_status`, + and :any:`Signal.config_status` properties return an error code that explains the problem. + For valid objects, Configuration Status returns success (no error). + + :any:`Cluster`, :any:`Frame<_frame.Frame>`, and :any:`Signal<_signal.Signal>` objects that became + invalid after the database is opened are still returned from the + :any:`Database.clusters`, :any:`Cluster.frames`, and :any:`Frame.mux_static_signals`, + even if :any:`Database.show_invalid_from_open` is ``False`` + and Configuration Status returns an error code. + For example, if you open a :any:`Frame<_frame.Frame>` with valid properties, + then you set :any:`Signal.start_bit` beyond the :any:`Frame.payload_len`, + the :any:`Frame.config_status` returns an error, but the frame is returned from :any:`Cluster.frames`. + """ return _props.get_database_show_invalid_from_open(self._handle) @show_invalid_from_open.setter diff --git a/nixnet_examples/can_dynamic_database_creation.py b/nixnet_examples/can_dynamic_database_creation.py index 7b3053e..f5aa5ae 100644 --- a/nixnet_examples/can_dynamic_database_creation.py +++ b/nixnet_examples/can_dynamic_database_creation.py @@ -33,12 +33,12 @@ def main(): frame.id = 1 frame.payload_len = 2 signal_1 = frame.mux_static_signals.add(signal_1_name) - signal_1.byte_ordr = constants.SigByteOrdr.BIG_ENDIAN + signal_1.byte_order = constants.SigByteOrder.BIG_ENDIAN signal_1.data_type = constants.SigDataType.UNSIGNED signal_1.start_bit = 0 signal_1.num_bits = 8 signal_2 = frame.mux_static_signals.add(signal_2_name) - signal_2.byte_ordr = constants.SigByteOrdr.BIG_ENDIAN + signal_2.byte_order = constants.SigByteOrder.BIG_ENDIAN signal_2.data_type = constants.SigDataType.UNSIGNED signal_2.start_bit = 8 signal_2.num_bits = 8 diff --git a/nixnet_examples/lin_dynamic_database_creation.py b/nixnet_examples/lin_dynamic_database_creation.py index c811c69..ed9897b 100644 --- a/nixnet_examples/lin_dynamic_database_creation.py +++ b/nixnet_examples/lin_dynamic_database_creation.py @@ -37,12 +37,12 @@ def main(): frame.id = 1 frame.payload_len = 2 signal_1 = frame.mux_static_signals.add(signal_1_name) - signal_1.byte_ordr = constants.SigByteOrdr.BIG_ENDIAN + signal_1.byte_order = constants.SigByteOrder.BIG_ENDIAN signal_1.data_type = constants.SigDataType.UNSIGNED signal_1.start_bit = 0 signal_1.num_bits = 8 signal_2 = frame.mux_static_signals.add(signal_2_name) - signal_2.byte_ordr = constants.SigByteOrdr.BIG_ENDIAN + signal_2.byte_order = constants.SigByteOrder.BIG_ENDIAN signal_2.data_type = constants.SigDataType.UNSIGNED signal_2.start_bit = 8 signal_2.num_bits = 8