From b216d61fa23ed29b8b40dc455c1534ed57bbd520 Mon Sep 17 00:00:00 2001 From: Damon Bohls Date: Sun, 11 Mar 2018 23:53:25 -0500 Subject: [PATCH] docs(DB): Finish all the database annotations/docs. Add all DB annotations and docstrings. Many imports moved into methods to avoid circular imports. Fixes #48 Fixes #209 Fixes some of #211 (annotations and docs) BREAKING CHANGE: Renamed: * Cluster.pd_us_reqd -> Cluster.pdus_reqd * Cluster.sig_refs -> Cluster.sigs * Ecu.clst_ref -> Ecu.clst * Ecu.rx_frm_refs -> Ecu.rx_frms * Ecu.tx_frm_refs -> Ecu.tx_frms * Ecu.linp_2min -> Ecu.lin_p2_min * Ecu.lins_tmin -> Ecu.lin_st_min * Frame.cluster_ref -> Frame.cluster * Frame.sig_refs -> Frame.sigs * Frame.mux_data_mux_sig_ref -> Frame.mux_data_mux_sig * Frame.pdu_refs -> Frame.pdus * LinSched.clst_ref -> LinSched.clst * Pdu.cluster_ref -> Pdu.cluster * Pdu.frm_refs -> Pdu.frms * Pdu.mux_data_mux_sig_ref -> Pdu.mux_data_mux_sig * Pdu.mux_static_sig_refs -> Pdu.mux_static_sigs * Signal.frame_ref -> Signal.frame * Signal.pdu_ref -> Signal.pdu * Signal.mux_subfrm_ref -> Signal.mux_subfrm * SubFrame.frm_ref -> SubFrame.frm * SubFrame.pdu_ref -> SubFrame.pdu Changed types: * Frame.application_protocol is now constants.AppProtocol * Frame.can_timing_type type is now constants.FrmCanTiming * Frame.lin_checksum type is now constants.FrmLinChecksum * All property renames that dropped "ref" now get/set the db object except for Cluster.database (opened issue #244) 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 | 204 +++++++- nixnet/_props.py | 32 +- nixnet/_session/j1939.py | 6 +- nixnet/database/_cluster.py | 251 +++++++++- nixnet/database/_collection.py | 2 +- nixnet/database/_ecu.py | 192 +++++++- nixnet/database/_frame.py | 451 +++++++++++++++++- nixnet/database/_lin_sched.py | 105 +++- nixnet/database/_lin_sched_entry.py | 115 ++++- nixnet/database/_pdu.py | 150 +++++- nixnet/database/_signal.py | 322 ++++++++++++- nixnet/database/_subframe.py | 102 +++- nixnet/database/database.py | 39 +- 23 files changed, 1896 insertions(+), 123 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 652fe84a..45a045b1 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 00000000..a92e14a1 --- /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 00000000..8fb2ecb0 --- /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 00000000..f0af6de3 --- /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 00000000..9f78580d --- /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_reqd( ref, # type: int ): # type: (...) -> bool return _cprops.get_database_bool( ref, - _cconsts.NX_PROP_CLST_PD_US_REQD, + _cconsts.NX_PROP_CLST_PDUS_REQD, ) @@ -4968,46 +4968,46 @@ def set_ecu_lin_function_id( ) -def get_ecu_linp_2min( +def get_ecu_lin_p2_min( ref, # type: int ): # type: (...) -> float return _cprops.get_database_f64( ref, - _cconsts.NX_PROP_ECU_LINP_2MIN, + _cconsts.NX_PROP_ECU_LIN_P2_MIN, ) -def set_ecu_linp_2min( +def set_ecu_lin_p2_min( ref, # type: int value, # type: float ): # type: (...) -> None _cprops.set_database_f64( ref, - _cconsts.NX_PROP_ECU_LINP_2MIN, + _cconsts.NX_PROP_ECU_LIN_P2_MIN, value, ) -def get_ecu_lins_tmin( +def get_ecu_lin_st_min( ref, # type: int ): # type: (...) -> float return _cprops.get_database_f64( ref, - _cconsts.NX_PROP_ECU_LINS_TMIN, + _cconsts.NX_PROP_ECU_LIN_ST_MIN, ) -def set_ecu_lins_tmin( +def set_ecu_lin_st_min( ref, # type: int value, # type: float ): # type: (...) -> None _cprops.set_database_f64( ref, - _cconsts.NX_PROP_ECU_LINS_TMIN, + _cconsts.NX_PROP_ECU_LIN_ST_MIN, value, ) diff --git a/nixnet/_session/j1939.py b/nixnet/_session/j1939.py index 1966816f..cae0df66 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 63587b3f..4714aa97 100644 --- a/nixnet/database/_cluster.py +++ b/nixnet/database/_cluster.py @@ -11,10 +11,7 @@ from nixnet.database import _collection from nixnet.database import _dbc_attributes -from nixnet.database import _ecu -from nixnet.database import _frame -from nixnet.database import _lin_sched -from nixnet.database import _pdu +from nixnet.database import _signal class Cluster(object): @@ -22,6 +19,10 @@ class Cluster(object): def __init__(self, handle): # type: (int) -> None + from nixnet.database import _ecu + from nixnet.database import _frame + from nixnet.database import _lin_sched + from nixnet.database import _pdu self._handle = handle self._dbc_attributes = None # type: typing.Optional[_dbc_attributes.DbcAttributeCollection] self._ecus = _collection.DbCollection( @@ -74,90 +75,293 @@ def merge( prefix, wait_for_complete): # type: (typing.Any, constants.Merge, typing.Text, bool) -> int + """Merges database objects and related subobjects from the source to this cluster. + + The source can be any of the following objects: + + * :any:`Frame<_frame.Frame>` + * :any:`Pdu` + * :any:`Ecu` + * :any:`LinSched` + * :any:`Cluster` + + All listed objects must have unique names in the cluster. + They are referenced here as objects, + as opposed to child objects (for example, a signal is a child of a frame). + + If the source object name is not used in the target cluster, + this function copies the source objects with the child objects to the target. + If an object with the same name exists in this cluster, + you can avoid name collisions by specifying the prefix to be added to the name. + + If an object with the same name exists in this cluster, + the merge behavior depends on the ``copy_mode`` input. + + **Example** + + Target frame F1(v1) has signals S1 and S2(v1). Source frame F1(v2) has signals S2(v2) and S3. + + (v1) and (v2) are two versions of one object with same name, but with different properties. + + * Result when ``copy_mode`` is ``COPY_USE_SOURCE``: F1(v2), S2(v2), S3. + * Result when ``copy_mode`` is ``COPY_USE_TARGET``: F1(v1), S1, S2(v1). + * Result when ``copy_mode`` is ``MERGE_USE_SOURCE``: F1(v2), S1, S2(v2), S3. + * Result when ``copy_mode`` is ``MERGE_USE_TARGET``: F1(v1), S1, S2(v1), S3. + + If the source object is a cluster, + this function copies all contained PDUs, ECUs, and LIN schedules + with their child objects to this cluster. + + Depending on the number of contained objects in the source and destination clusters, + the execution can take a longer time. + If ``wait_for_complete`` is ``True``, this function waits until the merging process gets completed. + If the execution completes without errors, + ``perecent_complete`` returns ``100``. + If ``wait_for_complete`` is ``False``, + the function returns quickly, + and ``perecent_complete`` returns values less than ``100``. + You must call :any:`Cluster.merge` repeatedly until ``perecent_complete`` returns ``100``. + You can use the time between calls to perform asynchronous tasks. + + Args: + source_obj(object): The object to be merged into this cluster. + copy_mode(:any:`Merge`): Defines the merging behavior if this cluster + already contains an object with the same name. + prefix(str): The prefix to be added to the source object name if an + object with the same name and type exists in this cluster. + wait_for_complete(bool): Determines whether the function returns directly + or waits until the entire transmission is completed. + Returns: + int: A value which indicates the merging progress as a percentage. ``100`` indicates completion. + """ return _funcs.nxdb_merge(self._handle, source_obj._handle, copy_mode.value, prefix, wait_for_complete) @property def baud_rate(self): + # type: (...) -> int + """int: Get or set the buad rate all custer nodes use. + + This baud rate represents the rate from the database, + so it is read-only from the session. + Use a session interface property (for example, :any:`Interface.baud_rate`) + to override the database baud rate with an application-specific baud rate. + + **CAN** + + For CAN, this rate can be 33333, 40000, 50000, 62500, 80000, 83333, + 100000, 125000, 160000, 200000, 250000, 400000, 500000, 800000, or + 1000000. Some transceivers may support only a subset of these values. + + **LIN** + + For LIN, this rate can be 2400-20000 inclusive. + + If you need values other than these, + use the custom settings as described in :any:`Interface.baud_rate`. + """ return _props.get_cluster_baud_rate64(self._handle) @baud_rate.setter def baud_rate(self, value): + # type: (int) -> None _props.set_cluster_baud_rate64(self._handle, value) @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the cluster object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_cluster_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_cluster_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the cluster object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured clusters in the database are not returned from + :any:`Database.clusters` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a cluster becomes invalid after the database has been opened, + the cluster still is returned from :any:`Database.clusters` even if + :any:`Database.show_invalid_from_open` to ``False``. + """ return _props.get_cluster_config_status(self._handle) @property def database_ref(self): - return _props.get_cluster_database_ref(self._handle) + # type: () -> int + # todo: return a Database object here + handle = _props.get_cluster_database_ref(self._handle) + return handle @property def dbc_attributes(self): # type: () -> _dbc_attributes.DbcAttributeCollection - """:any:`nixnet.database._dbc_attributes.DbcAttributeCollection`: Access the cluster's DBC attributes.""" + """:any:`DbcAttributeCollection`: Access the cluster's DBC attributes.""" if self._dbc_attributes is None: self._dbc_attributes = _dbc_attributes.DbcAttributeCollection(self._handle) return self._dbc_attributes @property def ecus(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`Ecu` objects in this cluster. + + An ECU is assigned to a cluster when the ECU object is created. + You cannot change this assignment afterwards. + """ return self._ecus @property def frames(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`Frame<_frame.Frame>` objects in this cluster. + + A frame is assigned to a cluster when the frame object is created. + You cannot change this assignment afterwards. + """ return self._frames @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the cluster object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + If you use a FIBEX file, the short name comes from the file. + If you use a CANdb (.dbc), LDF (.ldf), + or NI-CAN (.ncd) file, + no cluster name is stored in the file, + so NI-XNET uses the name Cluster. + If you create the cluster yourself, + the name that you provide is used. + + A cluster name must be unique for all clusters in a database. + + This short name does not include qualifiers to ensure that it is unique, + such as the database name. It is for display purposes. + """ return _props.get_cluster_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_cluster_name(self._handle, value) @property def pdus(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`Pdu<_pdu.Pdu>` objects in this cluster. + + A PDU is assigned to a cluster when the PDU object is created. + You cannot change this assignment afterwards. + """ return self._pdus @property - def pd_us_reqd(self): - return _props.get_cluster_pd_us_reqd(self._handle) + def pdus_reqd(self): + # type: () -> bool + """bool: Returns whether using :any:`PDUs` in the database API is required for this cluster. + + If this property returns ``False``, + it is safe to use signals as child objects of a frame without PDUs. + This behavior is compatible with NI-XNET 1.1 or earlier. + Clusters from .dbc, .ncd, or FIBEX 2 files always return ``False`` for this property, + so using PDUs from those files is not required. + + If this property returns ``True``, + the cluster contains PDU configuration, + which requires reading the PDUs as frame child objects and then signals as PDU child objects, + as shown in the following figure. + + Internally, the database always uses PDUs, + but shows the same signal objects also as children of a frame. + + .. image:: pdusrequired.gif + + | + + For this property to return ``False``, + the following conditions must be fulfilled for all frames in the cluster: + + * Only one PDU is mapped to the frame. + * This PDU is not mapped to other frames. + * The PDU Start Bit in the frame is 0. + * The PDU Update Bit is not used. + + If the conditions are not fulfilled for a given frame, + signals from the frame are still returned, + but reading the property returns a warning. + """ + return _props.get_cluster_pdus_reqd(self._handle) @property def protocol(self): + # type: () -> constants.Protocol + """:any:`Protocol`: Get or set the cluster protocol.""" return constants.Protocol(_props.get_cluster_protocol(self._handle)) @protocol.setter def protocol(self, value): + # type: (constants.Protocol) -> None _props.set_cluster_protocol(self._handle, value.value) @property - def sig_refs(self): - return _props.get_cluster_sig_refs(self._handle) + def sigs(self): + # type: () -> typing.Iterable[_signal.Signal] + """list of :any:`Signal<_signal.Signal>`: Returns a list of all :any:`Signal<_signal.Signal>` objects in this cluster.""" # NOQA: E501 + for handle in _props.get_cluster_sig_refs(self._handle): + yield _signal.Signal(handle) @property def can_io_mode(self): + # type: () -> constants.CanIoMode + """:any:`CanIoMode`: Get or set the CAN I/O Mode of the cluster.""" return constants.CanIoMode(_props.get_cluster_can_io_mode(self._handle)) @can_io_mode.setter def can_io_mode(self, value): + # type: (constants.CanIoMode) -> None _props.set_cluster_can_io_mode(self._handle, value.value) @property def can_fd_baud_rate(self): + # type: () -> int + """int: Get or set the fast data baud rate when :any:`Cluster.can_io_mode` is ``CanIoMode.CAN_FD_BRS``. + + Refer to the :any:`CanIoMode` for a description of ``CanIoMode.CAN_FD_BRS``. + Use a session interface property (for example, :any:`Interface.can_fd_baud_rate`) + to override the database fast baud rate with an application-specific fast baud rate. + + NI-XNET CAN hardware currently accepts the following numeric baud rates: + 200000, 250000, 400000, 500000, 800000, 1000000, 1250000, 1600000, + 2000000, 2500000, 4000000, 5000000, and 8000000. + Some transceivers may support only a subset of these values. + + If you need values other than these, + use the custom settings as described in :any:`Interface.can_fd_baud_rate`. + """ return _props.get_cluster_can_fd_baud_rate64(self._handle) @can_fd_baud_rate.setter def can_fd_baud_rate(self, value): + # type: (int) -> None _props.set_cluster_can_fd_baud_rate64(self._handle, value) @property @@ -430,14 +634,32 @@ def flex_ray_use_wakeup(self, value): @property def lin_schedules(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Returns a collection of :any:`LinSched` defined in this cluster. + + You assign a LIN schedule to a cluster when you create the LIN schedule object. + You cannot change this assignment afterwards. + The schedules in this collection are sorted alphabetically by schedule name. + """ return self._lin_sched @property def lin_tick(self): + # type: () -> float + """float: Returns the relative time between LIN ticks (relative f64 in seconds). + + The :any:`LinSchedEntry.delay` property must be a multiple of this tick. + + This tick is referred to as the "timebase" in the LIN specification. + + The :any:`Ecu.lin_master` property defines the Tick property in this cluster. + You cannot use the Tick property when there is no LIN Master property defined in this cluster. + """ return _props.get_cluster_lin_tick(self._handle) @lin_tick.setter def lin_tick(self, value): + # type: (float) -> None _props.set_cluster_lin_tick(self._handle, value) @property @@ -450,12 +672,21 @@ def flex_ray_alw_pass_act(self, value): @property def application_protocol(self): + # type: () -> constants.AppProtocol + """:any:`AppProtocol`: Get or set the application protocol.""" return constants.AppProtocol(_props.get_cluster_application_protocol(self._handle)) @application_protocol.setter def application_protocol(self, value): + # type: (constants.AppProtocol) -> None _props.set_cluster_application_protocol(self._handle, value.value) @property def can_fd_iso_mode(self): + # type: () -> constants.CanFdIsoMode + """:any:`CanFdIsoMode`: Returns the mode of a CAN FD cluster. + + The default is ``CanFdIsoMode.ISO``. + You define the value in a dialog box that appears when you define an alias for the database. + """ return constants.CanFdIsoMode(_props.get_cluster_can_fd_iso_mode(self._handle)) diff --git a/nixnet/database/_collection.py b/nixnet/database/_collection.py index 9e5158e0..b547e112 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 a89df3dd..712c360a 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 clst(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 rx_frms(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) + + @rx_frms.setter + def rx_frms(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 tx_frms(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) + + @tx_frms.setter + def tx_frms(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 a9738d84..973e699c 100644 --- a/nixnet/database/_frame.py +++ b/nixnet/database/_frame.py @@ -9,10 +9,10 @@ from nixnet import _props from nixnet import constants +from nixnet.database import _cluster from nixnet.database import _collection from nixnet.database import _dbc_attributes from nixnet.database import _signal -from nixnet.database import _subframe class Frame(object): @@ -20,6 +20,7 @@ class Frame(object): def __init__(self, handle): # type: (int) -> None + from nixnet.database import _subframe self._handle = handle self._dbc_attributes = None # type: typing.Optional[_dbc_attributes.DbcAttributeCollection] self._mux_static_signals = _collection.DbCollection( @@ -48,94 +49,353 @@ def __repr__(self): @property def application_protocol(self): - return _props.get_frame_application_protocol(self._handle) + # type: () -> constants.AppProtocol + """:any:`AppProtocol`: Get or set the frame's application protocol.""" + return constants.AppProtocol(_props.get_frame_application_protocol(self._handle)) @application_protocol.setter def application_protocol(self, value): - _props.set_frame_application_protocol(self._handle, value) + # type: (constants.AppProtocol) -> None + _props.set_frame_application_protocol(self._handle, value.value) @property - def cluster_ref(self): - return _props.get_frame_cluster_ref(self._handle) + def cluster(self): + # type: () -> _cluster.Cluster + """:any:`Cluster`: Get the parent cluster in which the frame has been created. + + You cannot change the parent cluster after the frame object has been created. + """ + handle = _props.get_frame_cluster_ref(self._handle) + return _cluster.Cluster(handle) @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the frame object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_frame_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_frame_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the frame object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured frames in the database are not returned from + :any:`Cluster.frames` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a frames becomes invalid after opening the database, + the frame still is returned from :any:`Cluster.frames` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_frame_config_status(self._handle) @property def default_payload(self): + # type: () -> typing.Iterable[int] + """list of int: Get or set the frame default payload, specified as a list of ints. + + Each int in the list represents a byte (U8). + The number of bytes in the list must match the :any:`Frame.payload_len` property. + + This property's initial value is an list of all ``0``, + except the frame is located in a CAN cluster with J1939 application protocol, + which uses ``0xFF`` by default. + For the database formats NI-XNET supports, + this property is not provided in the database file. + + When you use this frame within an NI-XNET session, + this property's use varies depending on the session mode. + The following sections describe this property's behavior for each session mode. + + Frame Output Single-Point and Frame Output Queued Modes: + Use this property when a frame transmits prior to a call to write. + This can occur when you set the :any:`SessionBase.auto_start` property to ``False`` + and start a session prior to writing. + When :any:`SessionBase.auto_start` is ``True`` (default), + the first frame write also starts frame transmit, so this property is not used. + + The following frame configurations potentially can transmit prior to a call to write: + + * :any:`Frame.can_timing_type` is ``CYCLIC_DATA``. + * :any:`Frame.can_timing_type` is ``CYCLIC_REMOTE``. + (for example, a remote frame received prior to a call to writing). + * :any:`Frame.can_timing_type` is ``EVENT_REMOTE``. + (for example, a remote frame received prior to a call to writing). + * :any:`Frame.can_timing_type` is ``CYCLIC_EVENT``. + * LIN frame in a schedule entry where :any:`LinSchedEntry.type` is ``UNCONDITIONAL``. + + The following frame configurations cannot transmit prior to writing, so this property is not used: + + * :any:`Frame.can_timing_type` is ``EVENT_DATA``.. + * LIN frame in a schedule entry where :any:`LinSchedEntry.type` is ``SPORADIC`` + or ``EVENT_TRIGGERED``. + + Frame Output Stream Mode: + This property is not used. Transmit is limited to frames provided to write. + + Signal Output Single-Point, Signal Output Waveform, and Signal Output XY Modes: + Use this property when a frame transmits prior to a call to write. + Refer to Frame Output Single-Point and Frame Output Queued Modes + for a list of applicable frame configurations. + + This property is used as the initial payload, + then each XNET Signal Default Value is mapped into that payload, + and the result is used for the frame transmit. + + Frame Input Stream and Frame Input Queued Modes: + This property is not used. + These modes do not return data prior to receiving frames. + + Frame Input Single-Point Mode: + This property is used for frames read returns prior to receiving the first frame. + + Signal Input Single-Point, Signal Input Waveform, and Signal Input XY Modes: + This property is not used. + Each :any:`Signal.default` 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_ext_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_ext_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 sigs(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): + # 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): + # 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.FrmCanTiming + """:any:`FrmCanTiming`: 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.FrmCanTiming(_props.get_frame_can_timing_type(self._handle)) @can_timing_type.setter def can_timing_type(self, value): + # type: (constants.FrmCanTiming) -> None _props.set_frame_can_timing_type(self._handle, value.value) @property def can_tx_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): + # type: (float) -> None _props.set_frame_can_tx_time(self._handle, value) @property @@ -216,64 +476,221 @@ def flex_ray_in_cyc_rep_ch_assigns(self, value): @property def lin_checksum(self): - return _props.get_frame_lin_checksum(self._handle) + # type: () -> constants.FrmLinChecksum + """:any:`FrmLinChecksum`: 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.FrmLinChecksum(_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_sig(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.sigs`. + """ return self._mux_static_signals @property def mux_subframes(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of :any:`SubFrame` objects in this frame. + + A subframe defines a group of signals transmitted using the same multiplexer value. + Only one subframe at a time is transmitted in the frame. + + A subframe is defined by creating a subframe object as a child of a frame. + """ return self._mux_subframes @property - def pdu_refs(self): - return _props.get_frame_pdu_refs(self._handle) + def pdus(self): + # actually returns typing.Iterable[_pdu.Pdu], but avoiding a circular import + # type: () -> typing.Iterable[typing.Any] + """list of :any:`Pdu`: Get or set a list that maps existing PDUs to a frame. + + A mapped PDU is transmitted inside the frame payload when the frame is transmitted. + You can map one or more PDUs to a frame and one PDU to multiple frames. + + Mapping PDUs to a frame requires setting three frame properties. + All three properties are lists of values: - @pdu_refs.setter - def pdu_refs(self, value): - _props.set_frame_pdu_refs(self._handle, value) + * :any:`Frame.pdus`: Set this property first to define + the sequence of values for the other two properties. + * :any:`Frame.pdu_start_bits`: Defines the start bit of the PDU inside the frame. + * :any:`Frame.pdu_update_bits`: Defines the update bit for the PDU inside the frame. + If the update bit is not used, set the value to ``-1``. + + Values on the same list position are corresponding. + For example, ``pdus[0]``, ``pdu_start_bits[0]``, + and ``pdu_update_bits[0]`` define the mapping for the first PDU in the frame. + + Databases imported from FIBEX prior to version 3.0, + from DBC, NCD, or LDF files have a strong one-to-one relationship between frames and PDUs. + Every frame has exactly one PDU mapped, and every PDU is mapped to exactly one frame. + + To unmap PDUs from a frame, set this property to an empty list. + A frame without mapped PDUs contains no signals. + + For CAN and LIN, NI-XNET supports only a one-to-one relationship between frames and PDUs. + For those interfaces, advanced PDU configuration returns + an error from the :any:`Frame.config_status` property and when creating a session. + If you do not use advanced PDU configuration, + you can avoid using PDUs in the database API + and create signals and subframes directly on a frame. + """ + from nixnet.database import _pdu + for handle in _props.get_frame_pdu_refs(self._handle): + yield _pdu.Pdu(handle) + + @pdus.setter + def pdus(self, value): + # value is actually typing.Iterable[_pdu.Pdu], but avoiding a circular import + # type: (typing.Iterable[typing.Any]) -> None + handle_list = [pdu._handle for pdu in value] + _props.set_frame_pdu_refs(self._handle, handle_list) @property def pdu_start_bits(self): + # type: () -> typing.Iterable[int] + """list of int: This property defines the start bits of PDUs mapped to a frame. + + A mapped PDU is transmitted inside the frame payload when the frame is transmitted. + You can map one or more PDUs to a frame and one PDU to multiple frames. + + Mapping PDUs to a frame requires setting of three frame properties. + All three properties are lists of values: + + * :any:`Frame.pdus`: Set this property first to define + the sequence of values for the other two properties. + * :any:`Frame.pdu_start_bits`: Defines the start bit of the PDU inside the frame. + * :any:`Frame.pdu_update_bits`: Defines the update bit for the PDU inside the frame. + If the update bit is not used, set the value to ``-1``. + + Values on the same list position are corresponding. + For example, ``pdus[0]``, ``pdu_start_bits[0]``, + and ``pdu_update_bits[0]`` define the mapping for the first PDU in the frame. + """ return _props.get_frame_pdu_start_bits(self._handle) @pdu_start_bits.setter def pdu_start_bits(self, value): + # type: (typing.List[int]) -> None _props.set_frame_pdu_start_bits(self._handle, value) @property def pdu_update_bits(self): + # type: () -> typing.Iterable[int] + """list of int: Get or set the update bits of PDUs mapped to a frame. + + If the update bit is not used for the PDU, set the value to -1. + The receiver uses the update bit to determine whether the frame sender has updated data in a particular PDU. + Update bits allow for the decoupling of a signal update from a frame occurrence. + Update bits is an optional PDU property. + + Mapping PDUs to a frame requires setting three frame properties. + All three properties are lists of values: + + * :any:`Frame.pdus`: Set this property first to define + the sequence of values for the other two properties. + * :any:`Frame.pdu_start_bits`: Defines the start bit of the PDU inside the frame. + * :any:`Frame.pdu_update_bits`: Defines the update bit for the PDU inside the frame. + If the update bit is not used, set the value to ``-1``. + + Values on the same list position are corresponding. + For example, ``pdus[0]``, ``pdu_start_bits[0]``, + and ``pdu_update_bits[0]`` define the mapping for the first PDU in the frame. + """ return _props.get_frame_pdu_update_bits(self._handle) @pdu_update_bits.setter def pdu_update_bits(self, value): + # type: (typing.List[int]) -> None _props.set_frame_pdu_update_bits(self._handle, value) @property def variable_payload(self): + # type: () -> bool + # This property is currently not documented in the C API. + # If/when we have C API documentation, we should add it here too. return _props.get_frame_variable_payload(self._handle) @variable_payload.setter def variable_payload(self, value): + # type: (bool) -> None _props.set_frame_variable_payload(self._handle, value) @property def can_io_mode(self): + # type: () -> constants.CanIoMode + """:any:`CanIoMode`: Get or set the frame's I/O mode. + + This property is used in ISO CAN FD+BRS mode only. + In this mode, + you can specify every frame to be transmitted in CAN 2.0, CAN FD, or CAN FD+BRS mode. + CAN FD+BRS frames require the interface to be in CAN FD+BRS mode; + otherwise, it is transmitted in CAN FD mode. + + When the interface is in Non-ISO CAN FD or Legacy ISO CAN FD mode, + this property is disregarded. + In Non-ISO CAN FD and Legacy ISO CAN FD mode, + you must use :any:`Interface.can_tx_io_mode` to switch the transmit mode. + + When the assigned database does not define the property in ISO CAN FD mode, + the frames are transmitted with :any:`Interface.can_io_mode`. + """ return constants.CanIoMode(_props.get_frame_can_io_mode(self._handle)) @can_io_mode.setter def can_io_mode(self, value): + # type: (constants.CanIoMode) -> None _props.set_frame_can_io_mode(self._handle, value.value) diff --git a/nixnet/database/_lin_sched.py b/nixnet/database/_lin_sched.py index 6f947030..35bc13de 100644 --- a/nixnet/database/_lin_sched.py +++ b/nixnet/database/_lin_sched.py @@ -8,14 +8,16 @@ 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 import _lin_sched_entry self._handle = handle self._entries = _collection.DbCollection( self._handle, @@ -43,45 +45,142 @@ 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 clst(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) + return _cluster.Cluster(handle) @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 08471928..4c644e27 100644 --- a/nixnet/database/_lin_sched_entry.py +++ b/nixnet/database/_lin_sched_entry.py @@ -4,12 +4,17 @@ import typing # NOQA: F401 +from nixnet import _cconsts +from nixnet import _errors 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 @@ -36,31 +41,78 @@ def __repr__(self): @property def collision_res_sched(self): - return _props.get_lin_sched_entry_collision_res_sched(self._handle) + # 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. + + Raises: + XnetError: The property requires that :any:`LinSchedEntry.type` be set to ``EVENT_TRIGGERED``. + """ + handle = _props.get_lin_sched_entry_collision_res_sched(self._handle) + if handle == 0: + # A bit of an abuse of errors + _errors.check_for_error(_cconsts.NX_ERR_DATABASE_OBJECT_NOT_FOUND) + return _lin_sched.LinSched(handle) @collision_res_sched.setter def collision_res_sched(self, value): - _props.set_lin_sched_entry_collision_res_sched(self._handle, 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_res_sched` to process the frames. + """ for ref in _props.get_lin_sched_entry_frames(self._handle): yield _frame.Frame(ref) @@ -72,32 +124,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) + # 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): + # 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): + # 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 67280c1c..c0c17c2b 100644 --- a/nixnet/database/_pdu.py +++ b/nixnet/database/_pdu.py @@ -2,19 +2,26 @@ from __future__ import division from __future__ import print_function +import typing # NOQA: F401 + from nixnet import _cconsts +from nixnet import _errors from nixnet import _props from nixnet import constants +from nixnet.database import _cluster from nixnet.database import _collection +from nixnet.database import _frame from nixnet.database import _signal -from nixnet.database import _subframe class Pdu(object): + """Database PDU""" def __init__(self, handle): # type: (int) -> None + from nixnet.database import _signal + from nixnet.database import _subframe self._handle = handle self._signals = _collection.DbCollection( self._handle, constants.ObjectClass.SIGNAL, _cconsts.NX_PROP_PDU_SIG_REFS, _signal.Signal) @@ -41,8 +48,14 @@ def __repr__(self): return '{}(handle={})'.format(type(self).__name__, self._handle) @property - def cluster_ref(self): - return _props.get_pdu_cluster_ref(self._handle) + def cluster(self): + # type: () -> _cluster.Cluster + """:any:`Cluster`: Get the parent cluster in which the PDU has been created. + + You cannot change the parent cluster after creating the PDU object. + """ + handle = _props.get_pdu_cluster_ref(self._handle) + return _cluster.Cluster(handle) @property def default_payload(self): @@ -54,52 +67,171 @@ def default_payload(self, value): @property def comment(self): + # type: () -> typing.Text + """str: Get or set a comment describing the PDU object. + + A comment is a string containing up to 65535 characters. + """ return _props.get_pdu_comment(self._handle) @comment.setter def comment(self, value): + # type: (typing.Text) -> None _props.set_pdu_comment(self._handle, value) @property def config_status(self): + # type: () -> int + """int: Returns the PDU object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured frames in the database are not returned from + :any:`Cluster.frames` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a frames becomes invalid after opening the database, + the frame still is returned from :any:`Cluster.frames` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_pdu_config_status(self._handle) @property - def frm_refs(self): - return _props.get_pdu_frm_refs(self._handle) + def frms(self): + # type: () -> typing.Iterable[_frame.Frame] + """list of :any:`Frame<_frame.Frame>`: Returns a list of all frames to which the PDU is mapped. + + A PDU is transmitted within the frames to which it is mapped. + + To map a PDU to a frame, + use the :any:`Frame.pdus`, + :any:`Frame.pdu_start_bits`, + and :any:`Frame.pdu_update_bits` properties. + You can map one PDU to multiple frames. + """ + for handle in _props.get_pdu_frm_refs(self._handle): + yield _frame.Frame(handle) @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the PDU object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A PDU name must be unique for all PDUs in a cluster. + """ return _props.get_pdu_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_pdu_name(self._handle, value) @property def payload_len(self): + # type: () -> int + """int: Get or set the size of the PDU data in bytes. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this PDU, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_pdu_payload_len(self._handle) @payload_len.setter def payload_len(self, value): + # type: (int) -> None _props.set_pdu_payload_len(self._handle, value) @property def signals(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of all :any:`Signal<_signal.Signal>` objects in this PDU. + + The collection includes all signals in the PDU, + including static and dynamic signals and the multiplexer signal. + """ return self._signals @property def mux_is_muxed(self): + # type: () -> bool + """bool: Returns ``True`` if the PDU contains a multiplexer signal. + + PDUs containing a multiplexer contain subframes that allow + using bits of the payload for different information (signals), + depending on the value of the :any:`SubFrame.mux_value` property. + """ return _props.get_pdu_mux_is_muxed(self._handle) @property - def mux_data_mux_sig_ref(self): - return _props.get_pdu_mux_data_mux_sig_ref(self._handle) + def mux_data_mux_sig(self): + # type: () -> _signal.Signal + """:any:`Signal<_signal.Signal>`: Data multiplexer signal in the PDU. + + This property returns the reference to the data multiplexer signal. + If data multiplexer is not defined in the PDU, the property raises an XnetError exception. + Use the :any:`Pdu.mux_is_muxed` property to determine whether the PDU contains a multiplexer signal. + + You can create a data multiplexer signal by creating a signal + and then setting the :any:`Signal.mux_is_data_mux` property to ``True``. + + A PDU can contain only one data multiplexer signal. + + Raises: + XnetError: The data multiplexer is not defined in the PDU. + """ + handle = _props.get_pdu_mux_data_mux_sig_ref(self._handle) + if handle == 0: + # A bit of an abuse of errors + _errors.check_for_error(_cconsts.NX_ERR_SIGNAL_NOT_FOUND) + return _signal.Signal(handle) @property - def mux_static_sig_refs(self): - return _props.get_pdu_mux_static_sig_refs(self._handle) + def mux_static_sigs(self): + # type: () -> typing.Iterable[_signal.Signal] + """list of :any:`Signal<_signal.Signal>`: Returns a list of static signals in the PDU. + + Returns an list of signal objects in the PDU that do not depend + on value of the :any:`SubFrame.mux_value` property. + Static signals are contained in every PDU transmitted, + as opposed to dynamic signals, + which are transmitted depending on the value of the :any:`SubFrame.mux_value` property. + + You can create static signals by specifying the PDU as the parent object. + You can create dynamic signals by specifying a subframe as the parent. + + If the PDU is not multiplexed, + this property returns the same list as the :any:`Pdu.signals` property. + """ + for handle in _props.get_pdu_mux_static_sig_refs(self._handle): + yield _signal.Signal(handle) @property def mux_subframes(self): + # type: () -> _collection.DbCollection + """:any:`DbCollection`: Collection of :any:`SubFrame` objects in this PDU. + + A subframe defines a group of signals transmitted using the same value of the :any:`SubFrame.mux_value`. + Only one subframe is transmitted in the PDU at a time. + """ return self._mux_subframes diff --git a/nixnet/database/_signal.py b/nixnet/database/_signal.py index fb68cc68..16281325 100644 --- a/nixnet/database/_signal.py +++ b/nixnet/database/_signal.py @@ -4,8 +4,11 @@ import typing # NOQA: F401 +from nixnet import _cconsts +from nixnet import _errors from nixnet import _props from nixnet import constants + from nixnet.database import _dbc_attributes from nixnet.database import _dbc_signal_value_table @@ -40,36 +43,100 @@ def __repr__(self): @property def byte_ordr(self): + # type: () -> constants.SigByteOrdr + """:any:`SigByteOrdr`: 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.SigByteOrdr(_props.get_signal_byte_ordr(self._handle)) @byte_ordr.setter def byte_ordr(self, value): + # type: (constants.SigByteOrdr) -> 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.sigs` 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.sigs` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_signal_config_status(self._handle) @property def data_type(self): + # type: () -> constants.SigDataType + """:any:`SigDataType`: Get or set the signal data type. + + This property determines how the bits of a signal in a frame must be interpreted to build a value. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this signal, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return constants.SigDataType(_props.get_signal_data_type(self._handle)) @data_type.setter def data_type(self, value): + # type: (constants.SigDataType) -> None _props.set_signal_data_type(self._handle, value.value) @property def dbc_attributes(self): # type: () -> _dbc_attributes.DbcAttributeCollection - """:any:`nixnet.database._dbc_attributes.DbcAttributeCollection`: Access the signal's DBC attributes.""" + """:any:`DbcAttributeCollection`: Access the signal's DBC attributes.""" if self._dbc_attributes is None: self._dbc_attributes = _dbc_attributes.DbcAttributeCollection(self._handle) return self._dbc_attributes @@ -77,109 +144,348 @@ def dbc_attributes(self): @property def dbc_signal_value_table(self): # type: () -> _dbc_signal_value_table.DbcSignalValueTable - """:any:`nixnet.database._dbc_signal_value_table.DbcSignalValueTable`: Access the signal's DBC value table.""" + """:any:`DbcSignalValueTable`: Access the signal's DBC value table.""" return self._dbc_signal_value_table @property def default(self): + # 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): + # type: (float) -> None _props.set_signal_default(self._handle, value) @property - def frame_ref(self): - return _props.get_signal_frame_ref(self._handle) + def frame(self): + # actually returns _frame.Frame, but avoiding a circular import + # type: () -> typing.Any + """:any:`Frame<_frame.Frame>`: Returns the signal parent frame object. + + The parent frame is defined when the signal object is created. You cannot change it afterwards. + """ + from nixnet.database import _frame + ref = _props.get_signal_frame_ref(self._handle) + return _frame.Frame(ref) @property def max(self): + # type: () -> float + """float: Get or set the scaled signal value maximum. + + Session read and write methods do not limit the signal value to a maximum value. + Use this database property to set the maximum value. + """ return _props.get_signal_max(self._handle) @max.setter def max(self, value): + # type: (float) -> None _props.set_signal_max(self._handle, value) @property def min(self): + # type: () -> float + """float: The scaled signal value minimum. + + Session read and write methods do not limit the signal value to a minimum value. + Use this database property to set the minimum value. + """ return _props.get_signal_min(self._handle) @min.setter def min(self, value): + # type: (float) -> None _props.set_signal_min(self._handle, value) @property def name(self): + # type: () -> typing.Text + """str: Get or set a string identifying a signal object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A signal name must be unique for all signals in a frame. + + This short name does not include qualifiers to ensure that it is unique, + such as the database, cluster, and frame name. + It is for display purposes. + """ return _props.get_signal_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_signal_name(self._handle, value) @property def name_unique_to_cluster(self): + # type: () -> typing.Text + """str: Returns a signal name unique to the cluster that contains the signal. + + If the single name is not unique within the cluster, + the name is .. + + You can pass the name to the `find` function to retrieve the reference to the object, + while the single name is not guaranteed success in `find` because it may be not unique in the cluster. + """ return _props.get_signal_name_unique_to_cluster(self._handle) @property def num_bits(self): + # type: () -> int + """int: The number of bits the signal uses in the frame payload. + + IEEE Float numbers are limited to 32 bit or 64 bit. + + Integer (signed and unsigned) numbers are limited to 1-52 bits. + NI-XNET converts all integers to doubles (64-bit IEEE Float). + Integer numbers with more than 52 bits + (the size of the mantissa in a 64-bit IEEE Float) + cannot be converted exactly to double, and vice versa; therefore, + NI-XNET does not support this. + + This property is required. + If the property does not contain a valid value, + and you create an XNET session that uses this signal, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_signal_num_bits(self._handle) @num_bits.setter def num_bits(self, value): + # type: (int) -> None _props.set_signal_num_bits(self._handle, value) @property - def pdu_ref(self): - return _props.get_signal_pdu_ref(self._handle) + def pdu(self): + # actually returns _pdu.Pdu, but avoiding a circular import + # type: () -> typing.Any + """:any:`Pdu`: Returns to the signal's parent PDU. + + The parent PDU is defined when the signal object is created. + You cannot change it afterwards. + """ + from nixnet.database import _pdu + ref = _props.get_signal_pdu_ref(self._handle) + return _pdu.Pdu(ref) @property def scale_fac(self): + # 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): + # type: (float) -> None _props.set_signal_scale_fac(self._handle, value) @property def scale_off(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): + # 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.dyn_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_subfrm(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 301f8b78..b77d3ad4 100644 --- a/nixnet/database/_subframe.py +++ b/nixnet/database/_subframe.py @@ -2,18 +2,22 @@ from __future__ import division from __future__ import print_function +import typing # NOQA: F401 + from nixnet import _cconsts from nixnet import _props from nixnet import constants from nixnet.database import _collection -from nixnet.database import _signal +from nixnet.database import _frame class SubFrame(object): + """Database subframe""" def __init__(self, handle): # type: (int) -> None + from nixnet.database import _signal self._handle = handle self._dyn_signals = _collection.DbCollection( self._handle, constants.ObjectClass.SIGNAL, _cconsts.NX_PROP_SUBFRM_DYN_SIG_REFS, _signal.Signal) @@ -39,36 +43,124 @@ def __repr__(self): @property def config_status(self): + # type: () -> int + """int: Returns the subframe object configuration status. + + Configuration Status returns an NI-XNET error code. + You can pass the value to the `nxStatusToString` function to + convert the value to a text description of the configuration problem. + + By default, incorrectly configured subframes in the database are not returned from + :any:`Frame.mux_subframes` because they cannot be used in the bus communication. + You can change this behavior by setting :any:`Database.show_invalid_from_open` to ``True``. + When the configuration status of a subframe becomes invalid after opening the database, + the subframe still is returned from :any:`Frame.mux_subframes` + even if :any:`Database.show_invalid_from_open` is ``False``. + """ return _props.get_subframe_config_status(self._handle) @property def dyn_signals(self): + # 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 frm(self): + # type: () -> _frame.Frame + """:any:`Frame<_frame.Frame>`: Returns the reference to the parent frame. + + The parent frame is defined when the subframe is created, + and you cannot change it afterwards. + """ + handle = _props.get_subframe_frm_ref(self._handle) + return _frame.Frame(handle) @property def mux_value(self): + # type: () -> int + """int: Get or set the multiplexer value for this subframe. + + This property specifies the multiplexer signal value used when the + dynamic signals in this subframe are transmitted in the frame. + Only one subframe is transmitted at a time in the frame. + + There also is a multiplexer value for a signal object as a read-only property. + It reflects the value set on the parent subframe object. + + This property is required. If the property does not contain a valid value, + and you create an XNET session that uses this subframe, + the session returns an error. + To ensure that the property contains a valid value, + you can do one of the following: + + * Use a database file (or alias) to create the session. + + The file formats require a valid value in the text for this property. + + * Set a value at runtime using this property. + + This is needed when you create your own in-memory database (*:memory:*) rather than use a file. + The property does not contain a default in this case, + so you must set a valid value prior to creating a session. + """ return _props.get_subframe_mux_value(self._handle) @mux_value.setter def mux_value(self, value): + # type: (int) -> None _props.set_subframe_mux_value(self._handle, value) @property def name(self): + # type: () -> typing.Text + """str: Get or set the name of the subframe object. + + Lowercase letters, uppercase letters, numbers, + and the underscore (_) are valid characters for the short name. + The space ( ), period (.), and other special characters are not supported within the name. + The short name must begin with a letter (uppercase or lowercase) or underscore, and not a number. + The short name is limited to 128 characters. + + A subframe name must be unique for all subframes in a frame. + + This short name does not include qualifiers to ensure that it is unique, + such as the database, cluster, and frame name. It is for display purposes. + """ return _props.get_subframe_name(self._handle) @name.setter def name(self, value): + # type: (typing.Text) -> None _props.set_subframe_name(self._handle, value) @property - def pdu_ref(self): - return _props.get_subframe_pdu_ref(self._handle) + def pdu(self): + # actually returns _pdu.Pdu, but avoiding a circular import + # type: () -> typing.Any + """:any:`Pdu`: Returns the subframe's parent PDU. + + This property returns the reference to the subframe's parent PDU. + The parent PDU is defined when the subframe object is created. + You cannot change it afterwards. + """ + from nixnet.database import _pdu + handle = _props.get_subframe_pdu_ref(self._handle) + return _pdu.Pdu(handle) @property def name_unique_to_cluster(self): + # type: () -> typing.Text + """str: Returns a subframe name unique to the cluster that contains the subframe. + + If the single name is not unique within the cluster, the name is .. + + You can pass the name to the `find` function to retrieve the reference to the object, + while the single name is not guaranteed success in `find` + because it may be not unique in the cluster. + """ return _props.get_subframe_name_unique_to_cluster(self._handle) diff --git a/nixnet/database/database.py b/nixnet/database/database.py index a33fdc23..0277bad3 100644 --- a/nixnet/database/database.py +++ b/nixnet/database/database.py @@ -11,7 +11,6 @@ from nixnet import constants from nixnet import errors -from nixnet.database import _cluster from nixnet.database import _collection @@ -32,6 +31,8 @@ def __init__(self, database_name): # type: (typing.Text) -> None self._handle = None # To satisfy `__del__` in case nxdb_open_database throws self._handle = _funcs.nxdb_open_database(database_name) + + from nixnet.database import _cluster self._clusters = _collection.DbCollection( self._handle, constants.ObjectClass.CLUSTER, _cconsts.NX_PROP_DATABASE_CLST_REFS, _cluster.Cluster) @@ -101,12 +102,11 @@ def close(self, close_all_refs=False): """ if self._handle is None: warnings.warn( - 'Attempting to close NI-XNET system but system was already ' + 'Attempting to close NI-XNET database but database was already ' 'closed', errors.XnetResourceWarning) return _funcs.nxdb_close_database(self._handle, close_all_refs) - self._handle = None def save(self, db_filepath=""): @@ -160,6 +160,39 @@ def clusters(self): @property def show_invalid_from_open(self): # type: () -> bool + """bool: Show or hide :any:`Frame<_frame.Frame>` and :any:`Signal<_signal.Signal>` objects that are invalid. + + After opening a database, this property always is set to ``False``, + meaning that invalid :any:`Cluster`, :any:`Frame<_frame.Frame>`, + and :any:`Signal<_signal.Signal>` objects + are not returned in properties that return a :any:`DbCollection` for the database + (for example, :any:`Cluster.frames` and :any:`Frame.mux_static_signals`). + Invalid :any:`Cluster`, :any:`Frame<_frame.Frame>`, + and :any:`Signal<_signal.Signal>` objects are incorrectly defined + and therefore cannot be used in the bus communication. + The ``False`` setting is recommended when you use the database to create XNET sessions. + + In case the database was opened to correct invalid configuration + (for example, in a database editor), + you must set the property to ``True`` prior to reading properties that return + a :any:`DbCollection` for the database + (for example, :any:`Cluster.frames` and :any:`Frame.mux_static_signals`). + + For invalid objects, + the :any:`Cluster.config_status`, + :any:`Frame.config_status`, + and :any:`Signal.config_status` properties return an error code that explains the problem. + For valid objects, Configuration Status returns success (no error). + + :any:`Cluster`, :any:`Frame<_frame.Frame>`, and :any:`Signal<_signal.Signal>` objects that became + invalid after the database is opened are still returned from the + :any:`Database.clusters`, :any:`Cluster.frames`, and :any:`Frame.mux_static_signals`, + even if :any:`Database.show_invalid_from_open` is ``False`` + and Configuration Status returns an error code. + For example, if you open a :any:`Frame<_frame.Frame>` with valid properties, + then you set :any:`Signal.start_bit` beyond the :any:`Frame.payload_len`, + the :any:`Frame.config_status` returns an error, but the frame is returned from :any:`Cluster.frames`. + """ return _props.get_database_show_invalid_from_open(self._handle) @show_invalid_from_open.setter