From 5efc92122156ad2dd7c5fa6f33ac926f6a82559b Mon Sep 17 00:00:00 2001 From: Damon Bohls Date: Sun, 11 Mar 2018 23:53:25 -0500 Subject: [PATCH 1/2] docs(DB): Finish all the database annotations/docs. Add all DB annotations and docstrings. Many imports moved into methods to avoid circular imports. Overloaded Database constructor's path param to allow construction from a ref (for the Cluster.database property). Fixes #48 Fixes #209 Fixes some of #211 (annotations and docs) BREAKING CHANGE: Renamed: * constants.FrmCanTiming -> constants.CanFrameTiming * constants.SigByteOrdr -> constants.SigByteOrder * constants.FrmLinChecksum -> constants.LinFrameChecksum * Cluster.database_ref -> Cluster.database * Cluster.pd_us_reqd -> Cluster.pdus_required * Cluster.sig_refs -> Cluster.signals * Ecu.clst_ref -> Ecu.cluster * Ecu.rx_frm_refs -> Ecu.frames_received * Ecu.tx_frm_refs -> Ecu.frames_transmitted * Ecu.linp_2min -> Ecu.lin_p2_min * Ecu.lins_tmin -> Ecu.lin_st_min * Frame.cluster_ref -> Frame.cluster * Frame.sig_refs -> Frame.signals * Frame.can_ext_id -> Frame.can_extended_id * Frame.can_tx_time -> Frame.can_transmit_time * Frame.mux_data_mux_sig_ref -> Frame.mux_data_mux_signal * Frame.pdu_refs -> Frame.pdus * LinSched.clst_ref -> LinSched.cluster * LinSchedEntry.collision_res_sched -> LinSchedEntry.collision_resolving_schedule * LinSchedEntry.nc_ff_data_bytes -> LinSchedEntry.node_config_free_format_data_bytes * LinSchedEntry.sched -> LinSchedEntry.schedule * Pdu.cluster_ref -> Pdu.cluster * Pdu.frm_refs -> Pdu.frames * Pdu.mux_data_mux_sig_ref -> Pdu.mux_data_mux_signal * Pdu.mux_static_sig_refs -> Pdu.mux_static_signals * Signal.byte_ordr -> Signal.byte_order * Signal.default -> Signal.default_value * Signal.frame_ref -> Signal.frame * Signal.pdu_ref -> Signal.pdu * Signal.frame_scale_fac -> Signal.scale_factor * Signal.frame_off -> Signal.scale_offset * Signal.mux_subfrm_ref -> Signal.mux_subframe * SubFrame.dyn_signals -> SubFrame.dynamic_signals * SubFrame.frm_ref -> SubFrame.frame * SubFrame.pdu_ref -> SubFrame.pdu Changed types: * Frame.application_protocol is now constants.AppProtocol * Frame.can_timing_type type is now constants.CanFrameTiming * Frame.lin_checksum type is now constants.LinFrameChecksum * All property renames that dropped "ref" now get/set the db object Removed: * IntfCanFdIsoMode (unused duplicate of CanFdIsoMode) --- docs/api_reference/bigendianstartbit12.gif | Bin 0 -> 1585 bytes docs/api_reference/database.rst | 4 + .../frameoverviewsignalstartingbit12.gif | Bin 0 -> 1957 bytes docs/api_reference/database/lin_sched.rst | 7 + .../database/lin_sched_entry.rst | 7 + docs/api_reference/database/pdu.rst | 7 + docs/api_reference/database/pdusrequired.gif | Bin 0 -> 3990 bytes docs/api_reference/database/subframe.rst | 7 + docs/api_reference/littleendianstartbit12.gif | Bin 0 -> 1622 bytes nixnet/_cconsts.py | 16 +- nixnet/_enums.py | 210 +++++++- nixnet/_props.py | 32 +- nixnet/_session/j1939.py | 6 +- nixnet/database/_cluster.py | 257 +++++++++- nixnet/database/_collection.py | 2 +- nixnet/database/_ecu.py | 192 +++++++- nixnet/database/_frame.py | 460 +++++++++++++++++- nixnet/database/_lin_sched.py | 108 +++- nixnet/database/_lin_sched_entry.py | 128 ++++- nixnet/database/_pdu.py | 157 +++++- nixnet/database/_signal.py | 347 ++++++++++++- nixnet/database/_subframe.py | 103 +++- nixnet/database/database.py | 53 +- .../can_dynamic_database_creation.py | 4 +- .../lin_dynamic_database_creation.py | 4 +- 25 files changed, 1947 insertions(+), 164 deletions(-) create mode 100644 docs/api_reference/bigendianstartbit12.gif create mode 100644 docs/api_reference/database/frameoverviewsignalstartingbit12.gif create mode 100644 docs/api_reference/database/lin_sched.rst create mode 100644 docs/api_reference/database/lin_sched_entry.rst create mode 100644 docs/api_reference/database/pdu.rst create mode 100644 docs/api_reference/database/pdusrequired.gif create mode 100644 docs/api_reference/database/subframe.rst create mode 100644 docs/api_reference/littleendianstartbit12.gif diff --git a/docs/api_reference/bigendianstartbit12.gif b/docs/api_reference/bigendianstartbit12.gif new file mode 100644 index 0000000000000000000000000000000000000000..fb25b49ad4a5b6eeb7a98097835ee1f508c37c1a GIT binary patch literal 1585 zcmV-12G03MNk%w1VVwYj0J8u900030|Nj60{{R3pGc#tOprmG*|1)NpGnvf)`|tz= z1poj4000000000000000A^8LW000XBEC2ui0G$AX000F4u*gZPy*TU5yZ>M~1%+as zXsWJk>%MR-&vdO6q>}7>@BhG{a7Zi~kI1AFS5i8k(5Q53Wa_BctadB4W>H2{_;kD$ z#bczIh#q>k>)d5<$ji*l!?n)Q z(p1X3HrLqM+S_0})v~au;CBe+=I7`N=IeM&{`~~@{Tp~7 zAOeB|4I=y(kX|l1dwOxoC+FZofC4WT%y?0u#*GgDiPee4XX3q6VJ!9wz%qb8kN#LP z~!(RY_h2nzb!Cu49kNbV?K{-J@Tf@)SC1Eya;+-{Os1w5ivXcVmjhxL039 ztSGhCAscmLU8ac<=T-W2Gv~%mBnIZ2a$x1dbB)R+JecuWXnrN99liDAVX&PO<~I9? zY7)_ZXNMs`ytwh>3XUr`o_zW8=FX9WR>G~i_3PKC|CYBd9`|zIzlY}zT=q{{_1KWb zyoZfhRGM%gcsTuQF|exsGnvX<`p7j^pPkWfxgX%A6zSP*rJXMf~XgO zHjWsXiM5%Q<7u6JSlEvON+=|KL{8>oit?>YE~L+s+P7&>#U<%C~Bd$ zuBvLUz>1owuD|js?6IsO`>V3C73gdKt<2&YY_ilYJL9XjswJzo*>d`o|JiQt~(d-H#96b;6SaUU}nvgHF2Vqw{T&*IcKbxajp&t$OFQ zyFMW6XQNK}?ykyS72LoF{{rE#7tguvRL6Wg@5>v%wqw9R-a7M^uYqm#x;uaR=O}Bw zIrgYz$XW#t!;8#e??8xUHe(&8MZGQUVv(L06PS3x6`-c;c{{HoQ-H?3uJpgLY ze(=Ma^xVh4%iRxR!i%5;@qoMjz3+br9H8>(cR&GhP=ojj9R@vUz~x=9d>8zn3ITY% z60R_VFT7UfL>R*Y!jOgHl3xm4$V2;`@P9?Ch|_~z1s&Ky8wyc|8|>i| zok+#xDNuh%WZ>?WIKeL72G4&rOyUTuSTu-Xaf)K3->bZ*#U`dvibUKX3LCgU3t~=r zG=$?DvuMW$9;%K^9OLwyrN=f3(vZ&!6(AR>J3{g?kYPmR_%L=xGWJo41l%JUCHY9} zWs;MdyyPLv_5%Z$@|36KgBMrXFh~jDm9Tu}0H#69S|Xtuv6Q7Pcgag2;4+u9?4>Yy z`HffxbC}4aWierS%w#&V8THWRGpEUnXbzy6)Xe4<(t}NHdXpNT{H8dazyuMP^PK2R jr#jb(fdC}HCh&}>Jm*QzdfM}z_{^t1_sLIsMgRaiL2f+! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5ce5cb20ad4d2102a6228a14e6a0cb4cab661fca GIT binary patch literal 1957 zcmV;W2U_??Nk%w1VWj|+0J8u900030|Nj60{{R3pGc#tOprmG*|1)NpGnvf)`|#uA z;{*f*|Ns900000000000A^8LW000aCEC2ui0Hpww000F45XecZy*TU5yZ>O=DP(D$ zXsWJk>%MR-&#Xk>c&_ifZ1=#Ra2WiZ7Dyy9*;^2wPov2Rr7En@tTrYsVXxq;QtTX) zk7z5E2`+oX>^14Tt&_U*WSQ=UhyQ?WZ)#$5gMWQ;c8hlaDPLH6er=L(Q-*7jk$!q^ zo{WrmfrOZbf{$pYke70+pP+W4tEQ@@w6vSJqlc!XuywMjc}j|zbZ3`dW3zB(zQ0g(+7?(gvN^7Hid_V@Vt>jCMV{{H|23LHqV zpuvL(6AmoE&(Vnj3ld12NU@^Dix@L%+{m$`$B!WYLo)0avZTqAC{wEJIMSgBh$~~t zoJmtgkAtXA+MQeF6>2b127~4~fcDiZLkDi%&K7RpOxyOVRl0W9>fM?*<69DOSK{T$R;t;;QbE!sjPb96 zz$yp#&B!>h)w+BYvvu1LCtk!`2Y(hlSMu4eos-sF&AK&f$BGg9{(ti6ZZ?Xp`@~4BB#Y%EnWFMZP@q^QOwLL(QJxIAyG(xm#yWe!O|--KE0? zTY2(%=+)7`#~vTPMDN=%mlZF+zy0$5(T5oSep$I^;D7Vwr(J>d2}mA+`{}1vg9-wO zV1N}S$l!hI0mj~e;ZeAug$`;M;eZ`JCYDwwl9(ZeBbqqjf-OSG--S4q2xE&fP81@C zx0Upxi!tsvBau9!XQPNo=J=wMMMBx6gG-i1+?7~nIb3u>hO__zV1_B?m|u=*W|(E3 zX=a*giaBJD2U3}0Y(OD!z@2#Ji6;SH>gng5eA@Y^o`DWZ=$l6R$X%6+LM5o7fc6P# zq>n;c=%femd83X}9?581;8l8O00A5->H(yh`e&(mUYcjBts>fKqbPRyWLu)L$||LL z>Kf~*u8;3)_^E_rojU6OuY>NI>9DV=S}m%v9vda6a@x8rwZvN6tGL$+ zo9na2GI=DN5-uBRw9t-AZn*K%E3KyL(u$;~bCUJ$wfBN6t+}htTWqA-PMK_t{e}hX zz^;x;YQWAy3@)k`C%kT=lPyb`vwlWwsK|qo{3pINZaizp4V$z80Wilb^UN>LY;(*t z-)!@jI`6EpzU(UHaZ_3rZS>Isap}@})TInG$GEmhrqokYji%LTD(zX*`#!xgu9!{> z^1LNqe6r9j6V2_yDzBZ@*!5a!cF1!ds-}?_ zZ2Jf@#qvU4Zr~W(jd5<9-`%@Y%Tr%j@5k1G%(^7;3)l=3sjsIN^oc`#QGS zUhb{exU;T$S-{eLF72bc9=yb&qnzIDR~^>2?u)lR`S7$$E_>|H8$UGj`Et&?@YD-V zx#(gST)XL8yWITl&4z8V@9Nh*{_&@)$aT=?_q?FLnqoTaSF-*o!vy}H)+YkWHR%kAmu0<7wS-o&am z(1`l8ge_&MN{4h)nwHe3CUxL8Wm?md8g--2Oy`7(sL^%;be}h6>P(O7RALeos92?H zK(%R6J~nl$KTRk;!-~|elGKx94Qf@VTGj`pm1rQ1>oP?;KeDd1sBKlKOYwSFyNa-@ z>Xa&9!K&7PCQhqo&1ze@no?Bm^s%}eD^dNLSj4&%k%g`7VkxUfn656Tpv56(72DU$ z?)9O7g{)amdrrOT^r@^R?Oz+a+Sk_hkW4gyZg;!ez{>Tv0;Fq21VG&45_bT)Nv?7~ zvLxdkce&6~jqXL7>s;hY_qx%ol5wqz-R>q=yT#?McgIT`Mm?9j=$(yu2O!?`x|b%O rYVUjJyAs#Z_r4&3jXw6v-~RgdzW|0uLqtH}0vq_i2u^SaA^-q8m|6N( literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3d17ad5e380b0c00a18858721b1bfe647bda6657 GIT binary patch literal 3990 zcmb`C_dnDR;K$#0&fQUQ_8w=H6(XIHoQy6qDxwl+WMz+BMh>@(vYnGH;fy;oiky+v ziH1>>^(m)?kD11Ozn|~l@O}O8{PFpGyw07sHZ=0Q3_1*&0{$D9%Uxez|MBC;!otFb z4<9BbCfICte}8{(Z|~EmPZ1`uqF) z`uci!csM&dJ2*Jl+1Z^tch1t%(#*_EUteEGM@LCXNkKtDR8&+*NJvmn5C((&Z}I=i z{|`X^gn$6x7@+l^>%UI`l#9lPVYH+(M;=Cu1{s%4)<)ShUSrID(`S8;?OkNzE@(fP z;Ize#hdf=_=!_2$f4#oneRpF07dQSkK7~LektwMm$!YiQXJ%#RU^Cn#MG9#}4~k1l z%gQS%s~%S7*3{Oy=YvG(438#eYa6klrM<4RtGg%i@w1wzeS^=Vf##vCfwA#%*2^^Z ztG7LqQ=~VubBvjJ{QHIF?2jw)OP|*$pT6KY-!~G!ZAPqb@8P!gLw0}t4L$f&WQa{NC458Om1KqqR}=KK0~_Tz`@Q z6RBj=z6YM_PSr1Y$<@d3V^`iq&QBPtXsg{YE_Wmg+@*;Bx!racsHF&4a>5M0IHiqr zW5nL%Um_e*5mn=$!7MV&O`Cv4^am%wgH29;60Qh&-6KK5n(m2tIp0fh`DD7d+sD&% z7z}}iC4m896*b69mMRba@bwqBg?jvgJOjzYZtnDLH2%d|WdJcoZ1DlYcyL!(#9II_ zpcIw;JvPeA^pS&!t=%-{p9yyXT^wS+KbYki>anh;xKCfXa}w?tlGSCg!D!qQKf9dr zopobz*lK8sa`2wB?mCIZccZuR%KfebEmr8cxOby9axV7`_R-<{Ka-LmlH03gHt)i- zWieoS4Oh{7T3t;^uf=NUIcPYWBnt&QEwYIFz(|F~7t;-?=-`Af7G)EukG>&MY?Yik zWMzfd6zPU5+ezJSBd4{0AWQJcsbSw;de5RI&t3MvL(x-mEcymMv550jhy9t^fPE4hCpBKM9{&ME}!lS>p_F~Wx6q&2`7P`On-UNxTYKF zBMGIf)*Ce08;qA@;f_nqUBIoECxb|vt5uV?bAv9ve``caf7h?{-SGYI^VT^_bUkj9 z;gXuoP{Uef%MTRC$#@9dHe~y0Kq}^Q9Yc95VoQMT1J~GKrHXcsn=}`_QtLP@ zv%KNx>%?SA9>3(asVL#%J;C%N95g@wz5LdJ*Soah%P4_x!Om>zNro;vfthR$KE3yC zYXS{BzCCFv5xo5hBT*76^|8_uKV-N%@AKNp#qK2U6~D%4uzX~EtJL!lSp&HSc7(t3 zk4H;EGu{IiPigs~ymX{zIueHG7=w)%aAoDiKJabs>xb{=;=ji%B()|z)`(sIAl#j* zqh~ljj~<6U%YNM4sXd>?ww|TcHydf(L-n`Fbf?M1M)u@2Va9_lbRV2(Y<enY!nE=RkcoX%fy*|=dZt>cUUrL+L&anhyc~vF= z>IwA=uRsym*B6zAIh2JZP*iGexw0CEvhoTUE!E($$Jy*Y&1M8k2SH$R;ie*I4iS~2 z7ED=W@}kvo;_FV>!FE=6^OCNRCZTM%+|Q8o2O(r01^Iihc(hn<6LCIjkh`n+EYtLa z%rC?8Lo+C*rnpj?Ow^*hkKdrA@P>jFd!Tx3wl@KnC3xxrRe}#RbdJq!6cngkNo0?( z65BQe5$tJMSjD+>lT1NHQ5A(_t_U7cM9Q$Vis515UbAB6m7L!e$oxVxvxeh2=@%>~ zZV%~e(?|0X;fnQoT`(a@^NhX8%A;ngz^9+&{1NqJIKLjq6|58s2F&?LIBB=fNr08B zv-xWd3>AQeKbR+-7_fkN^{?DZnPnkJd^G{fo*)bCXVV{WI9wE2l+9nQ8Un0x(rFXh{^zug$=j4|IR1y8BbwnpkIIYNhtwFG^V{&50 zmqS!FqBUyl)%{pe(T?qua_NgNGK;n!s@bU@JviFx4+#fzYX_I>AH@jWRP9eTDqhpL zbShV6_Y$$Vvd%S`4i68^sjXV7v#sO+ERaxL`^7*T7RKKAY<|4KC-FlTU&Km|8r1)3 zsWPyQoGLJ`x(_9KU9GkeHnon*alfbG>I_ZI9(()9q~w+ZpX8e}u6Yszy0`TCQ!k%w za%R{@mznU1xHJf~HzMD7M6Nl%9#jT~^WA_f5(8 zswpS^SR6^fgJ(wv@G1{n<_UVCqYW?eWS){I+Njg0|Sw8?MrCt;*|(-L~}mUc!?W-{PEq=g;5 zWd@Qok&gT>n8~lqJeS^NLzG@n9v39r_1xfF>HZU3h@X03Ji1`svKG(PnY$jF1}mG{-_=IgK3cQbHh%XvSZgagpEkHnp6mU5Udj z6u9Dyt-x@8@(1Cjoo9(Xl5{n`;5R*cLp8Ee!cu&pW()21$b%k>GyCa} z|IDxJ@A4%5<@9|1qtjuwI(zQU^{LOb7_Cx;FJvvLk$nn##q4k1W0%0N^Y@r>?_zM3 zHHlJ@CPdx1AlHh)EczzBtM9O|r5-P6-L4=fN)%{C=X`LKg89McWNj3d}Vnaxukm`>wQMqHHZb7)v0A zB`(aw$s=w#xe}czuv9X9_9XOSn}Pd25ep~%kx7W;$$v@2J~EM&Km${fB;$#Qlq@d_+5av;AUu9ZZtsuB1BBRq zO3jkieOJM(TG_(FRDz+kb98DBPOAt5zdmbM?Vq-Tg5z98%P{i4S7DK^lzRB_)>#;X zBGQaQa>e0r6HB!oPP&94tYXCRaiMmv+9d?$@Bmy{rx3iwGo@EKUQJbS@l#9vrQ&~GnF}%?elZ(yG-C^dQz6#~9%GH$2>x|4; zbIG&5de^Kj&-N-5vX4jq%=DoMpOD0LM8Hq^>TF%OK2-t3{y;&tD) z-U-=&d&FgX$5hGRYL;wOrFQY1Lh#vXsreQSNpN;l zra`g_Xpksg>gK7y`mSOREHGZ@xKe>K?1-?ekC z#7j|ai4Umv^8D6Sa^APZ+*c;3Lu$($G;FPU{ubh(SGq5HWlMxd7l@|Rc=Jz+yI+z(NB{+WrREwBkJp*}lUfzx zCxgtAgp7rGZi0|3w|`G4mS%&N3MwsZ9wyhp+(5`;s!x)+jCMz>j zVbajpNZ?L5C=gXOogF@7AQcl;H@`R#39Bu}yu6)BTx z69k7w`%KFV9jo@G)`_N4v4a3c9MFZirW)0D&%oXGfYt<|l7^VFPGedst7?1dQFnBG zUm!Jw)6llerg=$Z$hg!_1QETRX5H_ zH;UFa&80Ot+-&OPXJ7~oUu_tHs*IF0hVJg8uVYQ`j2pRQjL>7uy}-s98)o6BCcbp$ zTRJmoj4Ak)IT=V4k84)owJkbe6!kZEaGKNi>5A8dl}uVBD|yu(v}inS(VA<~{?(%U KwZ$9&O#TPaSt`i@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..78fcf0fdfb408e3d3b787bc11b7dfc414385d7d9 GIT binary patch literal 1622 zcmV-c2C4Z+Nk%w1VV(el0J8u900030|Nj60{{R3pGc#tOprmG*|1)NpGnvf)`|#uA z;{*f*|Ns900000000000A^8LW000aCEC2ui0GNl6=Z3i zXsWJk>%MR-&t;$#ORn#H@BhG{a7Zi~kH|&AhD$h~(5Q4uty-_xtai)oE^mvjcuX#v z&)IJi&2GEjW_6go%jWDl?Hqf@Z}(SyR&sEFRC9(?iB^J)PK!~9eteHtgl>?Le~pZp zihp``m4i&4Vy2Irhog?DPp+(#vZj}Gp$V|Ke0jHlT&|L&V3@b4!Lq)($)K~ArNnx$ zs#Vg!yvTv5wQ$_s-f!FA;^XAb!{zAd=;515?(gvN@^w$_z1Yk6j`#Wf`%d;m@%yKb z8Z&_iu?0-EYM?=f4HNo%m~i1SD7i8oEEtjFr+XIvW4+oqv17@P5((xDsZ0|@lpdvM zd?|Ay%SAI&QjFvnNlCB!NDo1}W#!kwuY4g<5gxsiZrH8r3S*C(MOh zsXi5{vTRnf9l@d%x|XO{w_{r>wAy8!-Me`4LQzW%f#1J?11|_n81Uf2g%2YZjP>W) zoqP`=I6%3w<;nyGU*1eP^JLDKJ%bMIG%Q)jblE=oJi4>y)~;E%77hDA=EkZWr%fHH zscgyt0+9ZFAoy_3!Y$LbY+QNiwp*2+dyMOM^5n5EUw2NtIp)`uw|~bC*R))3`SBL+8^LqBg6&GLsc=L@n)oHrnH=S6iMSDthn))ioc zG;#(bXhRB_V}d)Pw%{)WH0k7%P%Z%Flut@YC6$9+IpvWWCb`>^?c^oqm}LI2SAH0F z6<0=!5vEw1e!U5&oNca%=9=}X8OVLNLYpIT@`Uhx*tE$(lj5E&KV{g;g zYHhJCYTIn9Fgjagw4)Mw>8Z$`yPCSa#<;A$`C2F{i}hY>tib8kR^+w@|4Ev-x!6lB zwgk(h<(3miS#gyYJ89>%*_7$=$9XYKhPe?tOftFwiv_UBran9{$}EFK=rk_l3UkRg z->fseMg^5J&p89VbIm^!-Lt$8pDgs!MhD&W&>N5IrP5C`P4(3xPlPp!Tc@eAq^EU_ zC)X`U?Fr3dt628h!b(l{+g@AEwzO`?UG?2*&x?1~SHV5D-D0nd5y*rW{tn-D&HeY{ zYPa36(|JP-wc_Z#EVti{1CF@=Oon2vcjT6zH*@EjdoKCqRZSZP>5PA_c;@k{ehuWW zyKefIbr%b|&8WAoWbO#=UbOHr)BX75xAVTY--07w`t!cyethq+S8sdjz;|N!_S^@t zdF`h!4?XP7Q-6K*$~$Gz?9QVF4CET;7(zP^Qjk~dqYvx2z$Ye>kY=phAt(7rL>`TUOoXH*{dm4F zYLb#|1f>jT_Q^_$l8?C;@p&G0GB^1MHh4W64V)vcmwt3}!IN zg~wug?0W)8rZST`0ChO?nOm^OGMNd@YFcv&(wt^9ugOho24tDr?4~%K`AubpbDZcT z$3m!?&UTJtodY 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_required( 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..fdcb6d9 100644 --- a/nixnet/database/_cluster.py +++ b/nixnet/database/_cluster.py @@ -11,10 +11,8 @@ 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 +from nixnet.database import database as db # avoid conflict with property named 'database' class Cluster(object): @@ -22,6 +20,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 +76,296 @@ 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: () -> db.Database + """:any:`Database`: Returns the cluster parent database. + + The parent database is defined when the cluster object is created. You cannot change it afterwards. + """ + handle = _props.get_cluster_database_ref(self._handle) + return db.Database(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_required(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 +638,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 +676,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..1a2c060 100644 --- a/nixnet/database/_frame.py +++ b/nixnet/database/_frame.py @@ -9,10 +9,11 @@ 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 _pdu from nixnet.database import _signal -from nixnet.database import _subframe class Frame(object): @@ -20,6 +21,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 +50,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 +477,217 @@ 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): + # type: () -> typing.Iterable[_pdu.Pdu] + """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. + """ + for handle in _props.get_frame_pdu_refs(self._handle): + yield _pdu.Pdu(handle) + + @pdus.setter + def pdus(self, value): + # type: (typing.Iterable[_pdu.Pdu]) -> 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 + # CAR 690609: determine if this undocumented property should be documented. 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..77a6d86 100644 --- a/nixnet/database/_pdu.py +++ b/nixnet/database/_pdu.py @@ -2,19 +2,23 @@ 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 _collection -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 +45,16 @@ 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): + # actually returns _cluster.Cluster, but avoiding a circular import + # type: () -> typing.Any + """:any:`Cluster`: Get the parent cluster in which the PDU has been created. + + You cannot change the parent cluster after creating the PDU object. + """ + from nixnet.database import _cluster + handle = _props.get_pdu_cluster_ref(self._handle) + return _cluster.Cluster(handle) @property def default_payload(self): @@ -54,52 +66,177 @@ 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): + # actually returns _frame.Frame, but avoiding a circular import + # type: () -> typing.Iterable[typing.Any] + """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. + """ + from nixnet.database import _frame + 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): + # actually returns _signal.Signal, but avoiding a circular import + # type: () -> typing.Any + """: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. + """ + from nixnet.database import _signal + 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): + # actually returns typing.Iterable[_signal.Signal], but avoiding a circular import + # type: () -> typing.Iterable[typing.Any] + """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. + """ + from nixnet.database import _signal + 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..3363558 100644 --- a/nixnet/database/_signal.py +++ b/nixnet/database/_signal.py @@ -4,10 +4,14 @@ 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 +from nixnet.database import _pdu class Signal(object): @@ -39,37 +43,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 +145,346 @@ 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): + # type: () -> _pdu.Pdu + """: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. + """ + 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..3497000 100644 --- a/nixnet/database/_subframe.py +++ b/nixnet/database/_subframe.py @@ -2,18 +2,23 @@ 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 +from nixnet.database import _pdu 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 +44,122 @@ 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): + # type: () -> _pdu.Pdu + """: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. + """ + 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..d62a647 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 @@ -29,14 +28,23 @@ class Database(object): database_name(str): The database alias or file pathname to open. """ 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) + # type: (typing.Union[int, typing.Text]) -> None + self.db_needs_closing = False # To satisfy `__del__` in case nxdb_open_database throws + + if isinstance(database_name, int): + # overload the 'database_name' parameter so the Cluster.database property + # can construct a database object from a handle instead of a path. + self._handle = database_name + else: + self._handle = _funcs.nxdb_open_database(database_name) + self.db_needs_closing = True + + from nixnet.database import _cluster self._clusters = _collection.DbCollection( self._handle, constants.ObjectClass.CLUSTER, _cconsts.NX_PROP_DATABASE_CLST_REFS, _cluster.Cluster) def __del__(self): - if self._handle is not None: + if self.db_needs_closing: warnings.warn( 'Database was not explicitly closed before it was destructed. ' 'Resources on the device may still be reserved.', @@ -106,7 +114,7 @@ def close(self, close_all_refs=False): return _funcs.nxdb_close_database(self._handle, close_all_refs) - + self.db_needs_closing = False self._handle = None def save(self, db_filepath=""): @@ -160,6 +168,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 From dc2f5050ac6e6a7cd68f5ff1790b1ff679088816 Mon Sep 17 00:00:00 2001 From: Damon Bohls Date: Mon, 26 Mar 2018 11:47:15 -0500 Subject: [PATCH 2/2] Address Ed's comments. - Undo renaming of get_cluster_pdus_reqd - Move delayed imports to back references. - Hold off on implementing Cluster.database back reference. --- nixnet/_props.py | 2 +- nixnet/database/_cluster.py | 12 ++++-------- nixnet/database/_frame.py | 11 +++++++---- nixnet/database/_pdu.py | 19 +++++++------------ nixnet/database/_signal.py | 5 +++-- nixnet/database/_subframe.py | 5 +++-- nixnet/database/database.py | 18 +++++------------- 7 files changed, 30 insertions(+), 42 deletions(-) diff --git a/nixnet/_props.py b/nixnet/_props.py index 45d57a8..a1cc43b 100644 --- a/nixnet/_props.py +++ b/nixnet/_props.py @@ -2504,7 +2504,7 @@ def get_cluster_pdu_refs( ) -def get_cluster_pdus_required( +def get_cluster_pdus_reqd( ref, # type: int ): # type: (...) -> bool diff --git a/nixnet/database/_cluster.py b/nixnet/database/_cluster.py index fdcb6d9..632f4e0 100644 --- a/nixnet/database/_cluster.py +++ b/nixnet/database/_cluster.py @@ -12,7 +12,6 @@ from nixnet.database import _collection from nixnet.database import _dbc_attributes from nixnet.database import _signal -from nixnet.database import database as db # avoid conflict with property named 'database' class Cluster(object): @@ -201,13 +200,10 @@ def config_status(self): @property def database(self): - # type: () -> db.Database - """:any:`Database`: Returns the cluster parent database. - - The parent database is defined when the cluster object is created. You cannot change it afterwards. - """ + # type: () -> int + # todo: return a Database object here handle = _props.get_cluster_database_ref(self._handle) - return db.Database(handle) + return handle @property def dbc_attributes(self): @@ -313,7 +309,7 @@ def pdus_required(self): signals from the frame are still returned, but reading the property returns a warning. """ - return _props.get_cluster_pdus_required(self._handle) + return _props.get_cluster_pdus_reqd(self._handle) @property def protocol(self): diff --git a/nixnet/database/_frame.py b/nixnet/database/_frame.py index 1a2c060..7c168f4 100644 --- a/nixnet/database/_frame.py +++ b/nixnet/database/_frame.py @@ -12,7 +12,6 @@ from nixnet.database import _cluster from nixnet.database import _collection from nixnet.database import _dbc_attributes -from nixnet.database import _pdu from nixnet.database import _signal @@ -555,7 +554,8 @@ def mux_subframes(self): @property def pdus(self): - # type: () -> typing.Iterable[_pdu.Pdu] + # 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. @@ -588,12 +588,14 @@ def pdus(self): 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): - # type: (typing.Iterable[_pdu.Pdu]) -> None + # 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) @@ -658,7 +660,8 @@ def pdu_update_bits(self, value): @property def variable_payload(self): # type: () -> bool - # CAR 690609: determine if this undocumented property should be documented. + # 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 diff --git a/nixnet/database/_pdu.py b/nixnet/database/_pdu.py index 77a6d86..82fb6f7 100644 --- a/nixnet/database/_pdu.py +++ b/nixnet/database/_pdu.py @@ -9,7 +9,10 @@ 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 class Pdu(object): @@ -46,13 +49,11 @@ def __repr__(self): @property def cluster(self): - # actually returns _cluster.Cluster, but avoiding a circular import - # type: () -> typing.Any + # 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. """ - from nixnet.database import _cluster handle = _props.get_pdu_cluster_ref(self._handle) return _cluster.Cluster(handle) @@ -98,8 +99,7 @@ def config_status(self): @property def frames(self): - # actually returns _frame.Frame, but avoiding a circular import - # type: () -> typing.Iterable[typing.Any] + # 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. @@ -110,7 +110,6 @@ def frames(self): and :any:`Frame.pdu_update_bits` properties. You can map one PDU to multiple frames. """ - from nixnet.database import _frame for handle in _props.get_pdu_frm_refs(self._handle): yield _frame.Frame(handle) @@ -186,8 +185,7 @@ def mux_is_muxed(self): @property def mux_data_mux_signal(self): - # actually returns _signal.Signal, but avoiding a circular import - # type: () -> typing.Any + # type: () -> _signal.Signal """:any:`Signal<_signal.Signal>`: Data multiplexer signal in the PDU. This property returns the reference to the data multiplexer signal. @@ -202,7 +200,6 @@ def mux_data_mux_signal(self): Raises: XnetError: The data multiplexer is not defined in the PDU. """ - from nixnet.database import _signal handle = _props.get_pdu_mux_data_mux_sig_ref(self._handle) if handle == 0: # A bit of an abuse of errors @@ -211,8 +208,7 @@ def mux_data_mux_signal(self): @property def mux_static_signals(self): - # actually returns typing.Iterable[_signal.Signal], but avoiding a circular import - # type: () -> typing.Iterable[typing.Any] + # 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 @@ -227,7 +223,6 @@ def mux_static_signals(self): If the PDU is not multiplexed, this property returns the same list as the :any:`Pdu.signals` property. """ - from nixnet.database import _signal for handle in _props.get_pdu_mux_static_sig_refs(self._handle): yield _signal.Signal(handle) diff --git a/nixnet/database/_signal.py b/nixnet/database/_signal.py index 3363558..1308719 100644 --- a/nixnet/database/_signal.py +++ b/nixnet/database/_signal.py @@ -11,7 +11,6 @@ from nixnet.database import _dbc_attributes from nixnet.database import _dbc_signal_value_table -from nixnet.database import _pdu class Signal(object): @@ -293,12 +292,14 @@ def num_bits(self, value): @property def pdu(self): - # type: () -> _pdu.Pdu + # 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) diff --git a/nixnet/database/_subframe.py b/nixnet/database/_subframe.py index 3497000..cabf9c8 100644 --- a/nixnet/database/_subframe.py +++ b/nixnet/database/_subframe.py @@ -10,7 +10,6 @@ from nixnet.database import _collection from nixnet.database import _frame -from nixnet.database import _pdu class SubFrame(object): @@ -141,13 +140,15 @@ def name(self, value): @property def pdu(self): - # type: () -> _pdu.Pdu + # 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) diff --git a/nixnet/database/database.py b/nixnet/database/database.py index d62a647..0277bad 100644 --- a/nixnet/database/database.py +++ b/nixnet/database/database.py @@ -28,23 +28,16 @@ class Database(object): database_name(str): The database alias or file pathname to open. """ def __init__(self, database_name): - # type: (typing.Union[int, typing.Text]) -> None - self.db_needs_closing = False # To satisfy `__del__` in case nxdb_open_database throws - - if isinstance(database_name, int): - # overload the 'database_name' parameter so the Cluster.database property - # can construct a database object from a handle instead of a path. - self._handle = database_name - else: - self._handle = _funcs.nxdb_open_database(database_name) - self.db_needs_closing = True + # 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) def __del__(self): - if self.db_needs_closing: + if self._handle is not None: warnings.warn( 'Database was not explicitly closed before it was destructed. ' 'Resources on the device may still be reserved.', @@ -109,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.db_needs_closing = False self._handle = None def save(self, db_filepath=""):