From 784224d8c301179622f3360d4f9e06b04cbc1aa7 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 13:29:53 +0000 Subject: [PATCH 01/17] Add rig specific arduino device and data format + parser --- .bonsai/Bonsai.config | 12 +- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 97 ++- examples/rig.py | 9 +- local_packages/UclOpen.Core.0.1.2-dev0.nupkg | Bin 10256 -> 12950 bytes local_packages/UclOpen.Core.0.1.3-dev0.nupkg | Bin 12942 -> 0 bytes .../UclOpen.Devices.0.1.1-dev0.nupkg | Bin 7651 -> 0 bytes .../UclOpen.Devices.0.1.2-dev0.nupkg | Bin 7668 -> 7589 bytes .../UclOpen.Devices.0.1.3-dev0.nupkg | Bin 7664 -> 0 bytes .../UclOpen.Logging.0.1.0-dev0.nupkg | Bin 8090 -> 0 bytes .../UclOpen.Logging.0.1.2-dev0.nupkg | Bin 8109 -> 8039 bytes .../UclOpen.Logging.0.1.3-dev0.nupkg | Bin 8028 -> 0 bytes pyproject.toml | 2 +- src/DataSchemas/ucl_open_hf_visual.json | 199 +++-- src/Extensions/ParseMatrixSerialDevice.cs | 26 + src/Extensions/UclOpenHfVisual.Generated.cs | 812 ++++++++++-------- src/main.bonsai | 51 +- src/ucl_open_hf_visual/data_types.py | 9 + src/ucl_open_hf_visual/regenerate.py | 2 + src/ucl_open_hf_visual/rig.py | 4 +- src/ucl_open_hf_visual/visual_stimulation.py | 2 +- uv.lock | 4 +- 22 files changed, 744 insertions(+), 487 deletions(-) delete mode 100644 local_packages/UclOpen.Core.0.1.3-dev0.nupkg delete mode 100644 local_packages/UclOpen.Devices.0.1.1-dev0.nupkg delete mode 100644 local_packages/UclOpen.Devices.0.1.3-dev0.nupkg delete mode 100644 local_packages/UclOpen.Logging.0.1.0-dev0.nupkg delete mode 100644 local_packages/UclOpen.Logging.0.1.3-dev0.nupkg create mode 100644 src/Extensions/ParseMatrixSerialDevice.cs create mode 100644 src/ucl_open_hf_visual/data_types.py diff --git a/.bonsai/Bonsai.config b/.bonsai/Bonsai.config index bd63aed..88a88e9 100644 --- a/.bonsai/Bonsai.config +++ b/.bonsai/Bonsai.config @@ -40,9 +40,9 @@ - - - + + + @@ -108,9 +108,9 @@ - - - + + + diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 998d34c..144fd97 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index 0829d87..9f0e399 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -1,2 +1,97 @@  - \ No newline at end of file + + + + + + 344 + 310 + + + 334 + 64 + + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 361 + 396 + + + 334 + 64 + + Bonsai.Design.ObjectTextVisualizer + + + + + + + + + 511 + 236 + + + 334 + 64 + + Bonsai.Design.ObjectTextVisualizer + + + + + + + 500 + 144 + + + 334 + 64 + + Bonsai.Design.ObjectTextVisualizer + + + + + + + + + + false + + 640 + 283 + + + 334 + 184 + + Bonsai.Design.ObjectTextVisualizer + + + + + + + 174 + 146 + + + 837 + 213 + + Bonsai.Design.ObjectTextVisualizer + + + + + \ No newline at end of file diff --git a/examples/rig.py b/examples/rig.py index 76fb67f..6bbbc66 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -3,8 +3,8 @@ from ucl_open_hf_visual.rig import ( UclOpenHfVisualRig ) -from ucl_open.rigs.device import ( - SerialDeviceModule +from ucl_open_hf_visual.rig import ( + MatrixArduino ) from ucl_open.rigs.device import ( Screen, @@ -14,11 +14,10 @@ screen=Screen( texture_assets_directory="../textures" ), - arduino=SerialDeviceModule( + arduino=MatrixArduino( port_name="COM10", baud_rate=1000000, - new_line="\n", - pattern="%d" + new_line="\n" ) ) diff --git a/local_packages/UclOpen.Core.0.1.2-dev0.nupkg b/local_packages/UclOpen.Core.0.1.2-dev0.nupkg index acb92234ef9ef573371f10daf027abd9df8d6810..b31e0928b2d2569d16aab8d802bc64617452b0b8 100644 GIT binary patch delta 11325 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 bwKp?$`Ys0v_20D6|1$VLY^y)>f8758UoO~{ delta 8637 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 L=LaXcf3^M(_uY)f diff --git a/local_packages/UclOpen.Devices.0.1.1-dev0.nupkg b/local_packages/UclOpen.Devices.0.1.1-dev0.nupkg deleted file mode 100644 index 90a5ac062b4b5a9674ccffce595b00c5aadc966a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7651 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 Go&7Ix?gHWf diff --git a/local_packages/UclOpen.Devices.0.1.2-dev0.nupkg b/local_packages/UclOpen.Devices.0.1.2-dev0.nupkg index 2fa7e18e1eec622ea50961a10872759d5bd6e940..45d26bc7e989047db89fc250e1276194320f5a89 100644 GIT binary patch delta 6298 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 L;5?M>AMbwvP~rxt delta 6373 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 Y3vt(0!@&F}53Iih|1W``rTfSGAI5MZxc~qF diff --git a/local_packages/UclOpen.Devices.0.1.3-dev0.nupkg b/local_packages/UclOpen.Devices.0.1.3-dev0.nupkg deleted file mode 100644 index a414d4b631a586b8e6872a61e0ad1c72431cbc95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7664 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 Tz+aC*dfCxm40Md_udDw8@5T(M diff --git a/local_packages/UclOpen.Logging.0.1.0-dev0.nupkg b/local_packages/UclOpen.Logging.0.1.0-dev0.nupkg deleted file mode 100644 index dbae9da57ba73c487408b8a69e90d74e6c8b8bbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8090 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 N`^{@U@Fe-u`d`HQyy^e| diff --git a/local_packages/UclOpen.Logging.0.1.2-dev0.nupkg b/local_packages/UclOpen.Logging.0.1.2-dev0.nupkg index 4d8f3df4ef48dfd9a4b2b7b3fb46116db50f3600..25cb41e0b6aee079ac2c217fd1e2666d934f9d76 100644 GIT binary patch delta 6829 zcmai3bx<74lf~T$9^Bm_cyI{1!QGvp!DWM6a7!S#yW8TffhAaQhs75Q4o9x)e&2of z&rQ|5?wNi)H8nL;HPx?5VqY9zT?roH4Gatn3e0Irv_V!mUnWT+J{>Tl&|yJt9#dNw z2f+-29eJQgWGJpspZBsBi7y}n9`6^L>VI%cI}&t%&xisLSvJ#F|Jw6G&|bvj_SSfpI2rPjY&RH}Io^!MctnRAmYLZo!R4T5 zF&dmoB-ZJ7YsRmSLr$2J&}DXxa@ z_mzT9?#3d*3-hdnJ?FA(4P)7@l#2!!dr=cS@E)%~Ay|RPbWgfi`WFsoaNrDXpQ6%j z7IouJ`R<1l55)@59%uA963fs~SSiVVSiE)0^Gcny^eRTCj~a7D+Mml4oniX6`>jLx zf1lA>GM(fdEDX#OJPZsWOfr<57#OQz5I4j1=1JZhJFUAc>&j@yl5ZlV2DWgz7G|MK zu~Ko%R7AQzP8Xh%wvM{_%d)WDH=2Q*v$p;0hpQUOsbhPSR0!%oDuR+nlYr7{9U&6T7Ns0CG%Z*f8SjMX~1GeD;7X$`CwGYRGIOYWJING-5$Cp18=g8ib1!b%pJmQR(4hIX)rpwVW%tVEjL{3wnzgYuG zk@=PV5`mk7Nhc`0Sw33EHu$J02aorNSIJfGihPh87EI_{;xJCYGB@cmgDW?@gTwiG zh9s4Uqw~N7vhO7~0P{=t2j=1t) zXtFQXvPgy8NuHD#B@@ce(6Ue1zeT&U$EN+eJ9K33g>3c$kPx^mn%( zT}HV37%OBGP2s;6h2?;XQq%C!<<2i^wdi4JYXHpY8+ZO~Zx+oP^nje&(>%u3Gww)-=fY8wS7#hK zI)2Zt9__Ms@jbVg+D)Ta&1#mnvGUX;Detdh(7mdfPesL;*4!m@Y#I&jqdHq~undhX zU;r6_!-S+~iYr;EfP&84j&chl-lG8R)xnYShw1y_ccks*vCt0kqd2Pm4l?pM6Wh=B zJADBDa6hW}=RJ(vL1x z5+dd;ltUKyG|C^@1!bjUxpRIN2!vb{^t4;kM`w$K zh*rPxeY3*3Ds9UPr$$P&K$T^@zxf2(&JsKiLhXLJ5KF7MQ4euKj^>1d@?s*Vk@$Xn zeKvFG(AmscsH>D3kSF>1W&yd{+x^5c(0l#9Q#Dy4W}bTbB_Pr~ci{pZMdevl*E$op zV0dup&WxmDyEuMH63EA!kwqhJlZbKJEe%cOq-n(DBh0-pV*U;))WpQwKC-Fe>2xay z?edvtsbF1jVq@<)JL`QA?lJtHJjbJiZLEPU*(<7?^msPQi z*Ym~CG`vFYH4iR%XP+i}c6;3+lsK*>U?>$U*FP3-SJK=0#Tem+XDs;P z%>d&mn@4)-!{zJ~;vw2>YUu-^2R8F@dhgTQd(7xdpQ~}mCA_FF!V~@0G(HgG-19n* z?NXEb0((P$r5r6^P>OUq4oUCHl0OT6AiWo8rN7$Wc&52`aFHe57x2Km8tL)bSenY? z^{V>8S8=jllEI#2I0Z*k_0jkD=7Y?36jTZp>TJ@S&DnEl(rGK!jWV^Acimx656}g$ zdXNwSe&T2urH$FDYGFuj0r$1Y3-tPXn(F;qgH`fqzV%h2&XpSqa)a`Y&5V{3L+iip z=fT^Dvu(I_>eD}~NRUkxTMBmyB6ddqtN=G`255Dk!~wOE&I#u+6}47^l*Z!97ZC8_ z5Elt5LI*({-y@E6YZSu;!+o?C6U#lawp@@oO{Hh)tN2}vtL&1Lfz$%EY;VDg{g@rq z@D{Q&tbh~s^z!z&+y#dV7HAI`0k`JJTV5CnS78_I_8a4ODQmH2|Ll6oSQ5r}kCb?( z8av(1^f`M1VK_};K4rsg5}c{uvQm{7PRLZ<=;~Yki-NbHTnBV-H9Xh%h3sX)b6;GT z;52lsNR9#%S?X7qfD?L&F71gV9kdqX5kx9lEl)&rdF7?y%u#ZFT~)vOeNm4>T|(xx z6_A%;on%^OwlkZC3FI=TD$Xi+&(Kou)*pG#`_NzR`b=_p4Ou(_6Qs);pwQcGo3^0Y zBBg;#yz6gbFT>8x&SF_Fx8q*jgC(bd%v56Hxk%XVp6=v(KwZFO0s!_oyzLI;IPW1^ zumLJVzu;;y<{VGDB^6)MbhfZ67Uib?8fCG)ST-#>sjM0x&?My2_SxW9vuzBaU94)B zT3zoS8-;F=5Oh|>2&}DTb^d;!QPJ`A@UTmAOHAfrot61&CciSC$x|iUiB<6%T2&!f z?Ytr;GnPRD=%|4mvV@Qm63QXAh+>$4p+bnIIOCo4-Zd5{ZYYo^jKZO2A0ma8RTPE> zuZ?Ep*eGxsvA%0Mp_XSuj57TGT?MR5kFpUM+lSbaT8Tt_eBb*Umr601DS)JGqjN&( z?(w#@l~;hDUq~R4WGe1#r+)F+(#pJ~KID>E$3>hMsGm(7W+#pNalI$QUO}B7+Zl49 z)LvXhGIQ`*i^uE8%2?Ck0K^nu`d(QdXT4krhrqj6`rXxCN|{N7@#FCNi{xf%NXEno zw=t&G-2_eq0iX)ziC5ag-Nw=llDajB9+uhjgIsSlr$WgM@hEnTJ7v2B2C{`%{RB4< z%^hG0RN$2k6{m6r0&~_WceOe*CariDuZ~#9>0OByLU9$jl_hcOBq$WKNbIW+9N+2b z`{|jAEn-~OA9hJ_Y=dp(t%LZu7prPLF^ln3$Kq83*xSukHIyYwIfHzIKXUu9;*TRU@J=_21d6^L3oi?pFIQ%0()Zlg!< zipLOBVJ2TVI6y_`Xz6F#V*>=iqt5C#Q(;*SZJfn#jx;_tshn`k>R>Mf2(gd~RTc^m zI=Eqa-!q3Go7)NVea)p_pZ`{BDXKT9wF<>CNgpjgWD9D&okq2AjXL`h#R? zC5%50&7q$>Rlr}t8>oZlC8R;$wuPWqthZx;{_v4Z9XQUN|+sVT@Urbhp zSv$q7E~ExwV(VBl+y0sPj{~$NtYA%Gt!hmCPHBRtcPovC_#2|)mUJyZ!su*aLi3~W z1g0|TSQ5>9#uG_%UNCGPrLuFMCER^q#yp~@ejA4wPx?7s9~R56N5WsRH(2W>yws5ig#?5ek zH1*noO`t{NTrOHS6-Bg|)be3AAfl)lplIGY+*Gm-)&1rer-d!04ud@h>r^j(Y3s|F zzIvOtx)8cOAMUKBXel7AMp?AB15tnw+kS$v&XDVOE201{XB}$JfLur~1AA?mca+K< zT^e>7rDs#LL5=R<*ifaOp0;9ro(KApp+O8&q0!|7u@a%}O~X(gO?_Dc&~IAGx;4$Q z=F{L-qJjgulS8|!!KaQthdWB>@)?}U&FXX)QJW=d>Hb?XG4LQ}v4bYt-f+E-BPcuN zCzZxwI@cUg9FfZcuEyFG6Dqj?ymFMz^PQqn(F_hTea8`Qcwjh$}N9V=N{#%YfDqPEaXL#`A7z zzK#4#Q5MVr7K@Cj90)E-({0j`jp8XZhz)kC*=pusx-a;G+?f6c1`wL1CbJWOLr=}V zrYNW0eij+7FqcXlNhKF0{N^`*zqINSz%y9;2L?wTOt|EX@&ov7>%C~2BO+S~iEg3n z_2|Tpv?3noSUy{&KdY;t+1=tzNW0NV2w&KqB}B71D&O|_E{fsPg_`!0PcB(R4o*F- z`6EIS#t?NH9nGjn8_?^PN|7!EEkPbBhE`-}aKBO; zXx?W_z#3kg&w;%3+$t~Ety^r@ znWdLaIU?{UN0(_`q$-(P=Ed}T!(;}~VhK-GPv_HOl$)DLyr(aw7%XYW0ul#s>4o}}Q#GZPUVk6v*id~AojElIWr6Kc57kKmX2=79{0+C1V5~OpC3lJy87YI01RJTJ) zu|uXOYx0-&!Rz~Ox|*AeW^$q@PdA`3dleqeC9Qz8tQ%hY>~;zskaI@RVGLp=R~ULq z+aXuS2j-7_uBS~BPKHHjv?VKYZpWe#w1*Wwh${@UgfUab$Y+=9<>8Yr!#|cnqV=f4 zkQ~5jM%N5Oza#l9?@Jm^s~cjs&mfcOdi)(rYmIAznB~Vz()Eb%6o>j!GFG0uiY<|w z=hdfb6x(3_LeFMZU(Clhf?cMa_CzD-^L0EUGQbRNq~*M>CUm{8`F>5wsM!{X5HoUI zJbQ~(;;lo=SFpGK>)HJE>;kxD=I21%19QLs!t!5O`wN?|Fuwx=49xBGV2JA*iSW5x zC%fbkmhW5j;5K%C;tu%J*P=m=dX7tWsEP5(SN6-nKv2$~W>_^~KF?2ki)Ab76DVfr zXFCrAF?h4|FzF^I!hNkaV-4+4L9JCdo9DpC(^$n>K*p(zlg6mPTAP`?2p99H{QLyS zET9oIY$b)yybAF3&7U%V!QAKuyyPiq^!sy^yjA(w@DZA!V-QLYJz z2wf2_LbeJ~znTKVyt@irT^8>u&xmoJLtLcY0REZXYL8p`N+@?$=^pZHS4ri-TD zJ;hersOID+2528|U%kjLb0>1CI!#N>>0>}4$NYKzBO)PcM8~*4B6d%}pbQ5=bAIdU zCfFc_+m6Wd$7Do~%$OPW&p)gJ*$*7MczEKGQ ztg7-df4t(ex-fapuQod?HeA^eb3;yJgL)2DxIZ7M3QVi!twFi8OGfYWw*|f&7|q35 z($5Vg!Wb|UHt75zVAL3-tdkQuK&x9lfCDHh1ANuAhS5L-l{x_>NJdm#`Z`lwbljLn zaIuUHw#lwQ4*$|)I6FmR!nI4UpxQ`%{BWB3(?a$@gv$WfI~HUHpjZX%Y`h%Xc8t zwgE2LI+8|rlF{JdIDZ;lyy+iNd6A!HE%&dlxEYFpMP>`OXJ$mR?92VtyVu^!pEWaO zMA}Pgux`k_kkh^z(Y}k;q76CgX-+hQM2A@{)Kjc3O{?;Z8S>6vOygG3KFAAZTWTjb ziHTq8)Fg6p-vx-jkRQub6foB7)U1}oRsxDyT83JyYNtyTg@(@oj-N^ z;Homw8es`{u)3Myawv<>tYq#QUl6RurH>_cfmcRO6FXzVMy#ag$Ys1P;{&>NTy-4RSQP-o2Z#V6xeUrV7TlE@rn#{Y? zJ{zAKiD?_V@o#0~dI2o~8Q5_9UPogmNml|4V_|7tHtB(dCi6$f+m3!85i3TqKIh9% zI5%d27efj%no**#N5m75pDK%GmVt}`j!&wRo(0a;==DI6$;+~PdDXV+=R}Q7zN`2j zA8PGN736t-J*;FzC?5o$32k^GFJ7; zRiXbg-+q~Tlrp{#i@Bb@%blRIv>|nm{aoqMw*ePKWBf?w4@B{X(Ou6?3VP+Ja~y$& zdm0TJXPlsinA7L{_HM{2C#ZdL{acgb?_^b-r-T0qkT?H#NFp}oR3<}OO%^Bxx5XMc z*l8ymcDcR+_yS~tt%qZk8u#tGJ0o%S3}~AL-W$KU<#1-s$tYq$@i-??og6t+A4)Q z$K{@-jcP;!ae%Z!pV`=qz33kGitF)UvVL?+PhELxESHXb(D=g+XK25TLCa%Nc&qmpbHt@_BG zU`WhjIhW-3d|ff=vIl@7s4cyoJ<@rOJ|r+R&;YDrCy%3IVZh$q_Gy`svto`#dA*sn z)v#vmW9|2xktXB|{i3fQ7Hr%twj-yLm3Vy>vb%wI18UFr5(I}Y_x*l3ZP(9F3{N*B zyb+=`*k>Z^PCqN|gV}0Ol7j8SOAm=J3G|B1avs?>Y;9C}Qu7mmaCz+~&!Ju<*g!X< zh==QV+N=scLEgEPpFAba$>#3t|&C=eKmMA}`$C29jP zI081k*I1K+`vn^xY8eV!66~4qp+BcUJrjrJOAYKM91nFin)Cei+IJ!()HuA`Bb@x) za^zxOKGov$ba|^j_$y&9{;)l2Rm^9u0YPelP$w!}u1f0IdskZ$bIr}EzNtK+4?(cA z3Q&d+gJQlz2+ldo3yaJ8Pz7KKJ4gdg1s>Q2z5H~mD1_rJLW%A|1|xY<(zGLip@x*@ zwXqm%fUK-?i_v4bIoX?)cOZ^>p3X$6V2Eg&MxKIPGF*Tbnsl!(Lj*g;Gf@5!BNU$$ z`ei3K+7njf0=f4uSIe6MGdiA`2F{~v29j$i>1xwwne9% zU6g@dTWo)0xK>fp2nk&KE#i#eZpGWIFSV|bVpv^5xAeed7=G9@f>!SLX15jK5)9I? zdw$qzlmq2oJXv8Wm?%|}kr;+MaFDvOW}w){LIjE!ngnGx9$_6&EOO!HU*bS=9mxdT zX-Wf-6bbg#CP$EoQgQJJS-%sqHsj&v;o^HORG3?H3kh2BadTS=nDKK92{mk!HNv9) zM|?@nCFi32JNmcG@LEv*Pm@YMBd4eO%lW6+3)pZH(L89up}gq-uW{H(473-<=* zABiX02?bvFmGSkp|2xNoPx# delta 6835 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

PwV8`z!GkLCbnyqrp018C6Jk+ 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 diff --git a/local_packages/UclOpen.Logging.0.1.3-dev0.nupkg b/local_packages/UclOpen.Logging.0.1.3-dev0.nupkg deleted file mode 100644 index 3f8e185ad1f1bba43526d96efc1af90d182deca1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8028 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 CJ&YOv diff --git a/pyproject.toml b/pyproject.toml index 59d11aa..e107add 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,4 +62,4 @@ default-groups = ['dev'] required-version = '>=0.8.4' [tool.uv.sources] -ucl-open-rigs = {git = "https://github.com/ucl-open/ucl_open_rigs/", branch = "visual-displays"} \ No newline at end of file +ucl-open-rigs = {git = "https://github.com/ucl-open/ucl_open_rigs/", branch = "test_build"} \ No newline at end of file diff --git a/src/DataSchemas/ucl_open_hf_visual.json b/src/DataSchemas/ucl_open_hf_visual.json index 428f52a..8049347 100644 --- a/src/DataSchemas/ucl_open_hf_visual.json +++ b/src/DataSchemas/ucl_open_hf_visual.json @@ -121,81 +121,11 @@ "title": "Experiment", "type": "object" }, - "Screen": { + "MatrixArduino": { "properties": { "deviceType": { - "const": "Screen", - "default": "Screen", - "description": "Device type", - "title": "DeviceType", - "type": "string" - }, - "displayIndex": { - "default": 1, - "description": "Display index", - "title": "DisplayIndex", - "type": "integer" - }, - "targetRenderFrequency": { - "default": 60, - "description": "Target render frequency", - "title": "TargetRenderFrequency", - "type": "number" - }, - "targetUpdateFrequency": { - "default": 120, - "description": "Target update frequency", - "title": "TargetUpdateFrequency", - "type": "number" - }, - "textureAssetsDirectory": { - "default": "Textures", - "description": "Calibration directory", - "title": "TextureAssetsDirectory", - "type": "string" - }, - "calibration": { - "default": null, - "description": "Calibration parameters for a set of named display monitors for visual stimuli", - "oneOf": [ - { - "additionalProperties": { - "$ref": "#/$defs/DisplayCalibration" - }, - "type": "object" - }, - { - "type": "null" - } - ], - "title": "Calibration" - }, - "brightness": { - "default": 0, - "description": "Brightness", - "maximum": 1, - "minimum": -1, - "title": "Brightness", - "type": "number" - }, - "contrast": { - "default": 1, - "description": "Contrast", - "maximum": 1, - "minimum": -1, - "title": "Contrast", - "type": "number" - } - }, - "title": "Screen", - "type": "object" - }, - "SerialDeviceModule": { - "description": "Represents the SerialDevice workflow module.\n\nMirrors all externalized properties of SerialDevice.bonsai, including\nport configuration, framing, buffer settings, and parsing pattern.", - "properties": { - "deviceType": { - "const": "SerialDevice", - "default": "SerialDevice", + "const": "MatrixArduino", + "default": "MatrixArduino", "title": "DeviceType", "type": "string" }, @@ -213,12 +143,6 @@ "title": "BaudRate", "type": "integer" }, - "pattern": { - "default": "", - "description": "Pattern used to parse each incoming serial line (same syntax as Bonsai Parse/ScanPattern).", - "title": "Pattern", - "type": "string" - }, "encoding": { "default": null, "description": "Optional text encoding for interpreting incoming bytes.", @@ -314,7 +238,114 @@ "required": [ "portName" ], - "title": "SerialDeviceModule", + "title": "MatrixArduino", + "type": "object" + }, + "MatrixArduinoData": { + "properties": { + "encoderCount": { + "title": "EncoderCount", + "type": "integer" + }, + "lickCountLeft": { + "title": "LickCountLeft", + "type": "integer" + }, + "lickCountRight": { + "title": "LickCountRight", + "type": "integer" + }, + "lastSyncPulseTime": { + "title": "LastSyncPulseTime", + "type": "integer" + }, + "photodiodeVal": { + "title": "PhotodiodeVal", + "type": "integer" + }, + "currentMs": { + "title": "CurrentMs", + "type": "integer" + } + }, + "required": [ + "encoderCount", + "lickCountLeft", + "lickCountRight", + "lastSyncPulseTime", + "photodiodeVal", + "currentMs" + ], + "title": "MatrixArduinoData", + "type": "object" + }, + "Screen": { + "properties": { + "deviceType": { + "const": "Screen", + "default": "Screen", + "description": "Device type", + "title": "DeviceType", + "type": "string" + }, + "displayIndex": { + "default": 1, + "description": "Display index", + "title": "DisplayIndex", + "type": "integer" + }, + "targetRenderFrequency": { + "default": 60, + "description": "Target render frequency", + "title": "TargetRenderFrequency", + "type": "number" + }, + "targetUpdateFrequency": { + "default": 120, + "description": "Target update frequency", + "title": "TargetUpdateFrequency", + "type": "number" + }, + "textureAssetsDirectory": { + "default": "Textures", + "description": "Calibration directory", + "title": "TextureAssetsDirectory", + "type": "string" + }, + "calibration": { + "default": null, + "description": "Calibration parameters for a set of named display monitors for visual stimuli", + "oneOf": [ + { + "additionalProperties": { + "$ref": "#/$defs/DisplayCalibration" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Calibration" + }, + "brightness": { + "default": 0, + "description": "Brightness", + "maximum": 1, + "minimum": -1, + "title": "Brightness", + "type": "number" + }, + "contrast": { + "default": 1, + "description": "Contrast", + "maximum": 1, + "minimum": -1, + "title": "Contrast", + "type": "number" + } + }, + "title": "Screen", "type": "object" }, "UclOpenHfVisualRig": { @@ -329,7 +360,7 @@ "$ref": "#/$defs/Screen" }, "arduino": { - "$ref": "#/$defs/SerialDeviceModule", + "$ref": "#/$defs/MatrixArduino", "title": "Arduino" } }, @@ -414,17 +445,17 @@ "Vector3": { "properties": { "x": { - "description": "X coordinate of the point", + "description": "X coordinate of the point.", "title": "X", "type": "number" }, "y": { - "description": "Y coordinate of the point", + "description": "Y coordinate of the point.", "title": "Y", "type": "number" }, "z": { - "description": "Z coordinate of the point", + "description": "Z coordinate of the point.", "title": "Z", "type": "number" } diff --git a/src/Extensions/ParseMatrixSerialDevice.cs b/src/Extensions/ParseMatrixSerialDevice.cs new file mode 100644 index 0000000..f4134d1 --- /dev/null +++ b/src/Extensions/ParseMatrixSerialDevice.cs @@ -0,0 +1,26 @@ +using System; +using System.Reactive.Linq; +using Bonsai; +using Bonsai.Harp; +using UclOpenHfVisualDataSchema; + +public class ParseMatrixSerialDevice : Transform> +{ + public override IObservable> Process(IObservable source) + { + return source.Select(value => + { + var values = value.Split(','); + var matrixArduinoData = new MatrixArduinoData + { + EncoderCount = Convert.ToInt32(values[0]), + LickCountLeft = Convert.ToInt32(values[1]), + LickCountRight = Convert.ToInt32(values[2]), + LastSyncPulseTime = Convert.ToInt32(values[3]), + PhotodiodeVal = Convert.ToInt32(values[4]), + CurrentMs = Convert.ToInt32(values[5]), + }; + return new Timestamped(matrixArduinoData, matrixArduinoData.CurrentMs / 1000d); + }); + } +} \ No newline at end of file diff --git a/src/Extensions/UclOpenHfVisual.Generated.cs b/src/Extensions/UclOpenHfVisual.Generated.cs index 39afd30..6f418c9 100644 --- a/src/Extensions/UclOpenHfVisual.Generated.cs +++ b/src/Extensions/UclOpenHfVisual.Generated.cs @@ -435,53 +435,84 @@ public override string ToString() [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] - public partial class Screen + public partial class MatrixArduino { private string _deviceType; - private int _displayIndex; + private string _portName; - private double _targetRenderFrequency; + private int _baudRate; - private double _targetUpdateFrequency; + private string _encoding; - private string _textureAssetsDirectory; + private string _newLine; - private System.Collections.Generic.Dictionary _calibration; + private string _parity; - private double _brightness; + private int _parityReplace; - private double _contrast; + private int _dataBits; - public Screen() + private string _stopBits; + + private string _handshake; + + private bool _discardNull; + + private bool _dtrEnable; + + private bool _rtsEnable; + + private int _readBufferSize; + + private int _writeBufferSize; + + private int _receivedBytesThreshold; + + private string _serialMessageSubjectName; + + public MatrixArduino() { - _deviceType = "Screen"; - _displayIndex = 1; - _targetRenderFrequency = 60D; - _targetUpdateFrequency = 120D; - _textureAssetsDirectory = "Textures"; - _brightness = 0D; - _contrast = 1D; + _deviceType = "MatrixArduino"; + _baudRate = 9600; + _newLine = "\r\n"; + _parity = "None"; + _parityReplace = 63; + _dataBits = 8; + _stopBits = "One"; + _handshake = "None"; + _discardNull = false; + _dtrEnable = false; + _rtsEnable = false; + _readBufferSize = 4096; + _writeBufferSize = 2048; + _receivedBytesThreshold = 1; + _serialMessageSubjectName = "SerialMessages"; } - protected Screen(Screen other) + protected MatrixArduino(MatrixArduino other) { _deviceType = other._deviceType; - _displayIndex = other._displayIndex; - _targetRenderFrequency = other._targetRenderFrequency; - _targetUpdateFrequency = other._targetUpdateFrequency; - _textureAssetsDirectory = other._textureAssetsDirectory; - _calibration = other._calibration; - _brightness = other._brightness; - _contrast = other._contrast; + _portName = other._portName; + _baudRate = other._baudRate; + _encoding = other._encoding; + _newLine = other._newLine; + _parity = other._parity; + _parityReplace = other._parityReplace; + _dataBits = other._dataBits; + _stopBits = other._stopBits; + _handshake = other._handshake; + _discardNull = other._discardNull; + _dtrEnable = other._dtrEnable; + _rtsEnable = other._rtsEnable; + _readBufferSize = other._readBufferSize; + _writeBufferSize = other._writeBufferSize; + _receivedBytesThreshold = other._receivedBytesThreshold; + _serialMessageSubjectName = other._serialMessageSubjectName; } - ///

- /// Device type - /// [Newtonsoft.Json.JsonPropertyAttribute("deviceType")] - [System.ComponentModel.DescriptionAttribute("Device type")] public string DeviceType { get @@ -495,145 +526,306 @@ public string DeviceType } /// - /// Display index + /// The name of the device serial port. /// - [Newtonsoft.Json.JsonPropertyAttribute("displayIndex")] - [System.ComponentModel.DescriptionAttribute("Display index")] - public int DisplayIndex + [Newtonsoft.Json.JsonPropertyAttribute("portName", Required=Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DescriptionAttribute("The name of the device serial port.")] + public string PortName { get { - return _displayIndex; + return _portName; } set { - _displayIndex = value; + _portName = value; } } /// - /// Target render frequency + /// Baud rate for serial communication. /// - [Newtonsoft.Json.JsonPropertyAttribute("targetRenderFrequency")] - [System.ComponentModel.DescriptionAttribute("Target render frequency")] - public double TargetRenderFrequency + [Newtonsoft.Json.JsonPropertyAttribute("baudRate")] + [System.ComponentModel.DescriptionAttribute("Baud rate for serial communication.")] + public int BaudRate { get { - return _targetRenderFrequency; + return _baudRate; } set { - _targetRenderFrequency = value; + _baudRate = value; } } /// - /// Target update frequency + /// Optional text encoding for interpreting incoming bytes. /// - [Newtonsoft.Json.JsonPropertyAttribute("targetUpdateFrequency")] - [System.ComponentModel.DescriptionAttribute("Target update frequency")] - public double TargetUpdateFrequency + [Newtonsoft.Json.JsonPropertyAttribute("encoding")] + [System.ComponentModel.DescriptionAttribute("Optional text encoding for interpreting incoming bytes.")] + public string Encoding { get { - return _targetUpdateFrequency; + return _encoding; } set { - _targetUpdateFrequency = value; + _encoding = value; } } /// - /// Calibration directory + /// Line termination sequence used to delimit incoming messages. /// - [Newtonsoft.Json.JsonPropertyAttribute("textureAssetsDirectory")] - [System.ComponentModel.DescriptionAttribute("Calibration directory")] - public string TextureAssetsDirectory + [Newtonsoft.Json.JsonPropertyAttribute("newLine")] + [System.ComponentModel.DescriptionAttribute("Line termination sequence used to delimit incoming messages.")] + public string NewLine { get { - return _textureAssetsDirectory; + return _newLine; } set { - _textureAssetsDirectory = value; + _newLine = value; } } /// - /// Calibration parameters for a set of named display monitors for visual stimuli + /// Parity checking mode for the serial port. /// - [System.Xml.Serialization.XmlIgnoreAttribute()] - [Newtonsoft.Json.JsonPropertyAttribute("calibration")] - [System.ComponentModel.DescriptionAttribute("Calibration parameters for a set of named display monitors for visual stimuli")] - public System.Collections.Generic.Dictionary Calibration + [Newtonsoft.Json.JsonPropertyAttribute("parity")] + [System.ComponentModel.DescriptionAttribute("Parity checking mode for the serial port.")] + public string Parity { get { - return _calibration; + return _parity; } set { - _calibration = value; + _parity = value; } } /// - /// Brightness + /// Byte used to replace invalid bytes detected by a parity error. /// - [Newtonsoft.Json.JsonPropertyAttribute("brightness")] - [System.ComponentModel.DescriptionAttribute("Brightness")] - public double Brightness + [Newtonsoft.Json.JsonPropertyAttribute("parityReplace")] + [System.ComponentModel.DescriptionAttribute("Byte used to replace invalid bytes detected by a parity error.")] + public int ParityReplace { get { - return _brightness; + return _parityReplace; } set { - _brightness = value; + _parityReplace = value; } } /// - /// Contrast + /// Number of data bits per serial frame. /// - [Newtonsoft.Json.JsonPropertyAttribute("contrast")] - [System.ComponentModel.DescriptionAttribute("Contrast")] - public double Contrast + [Newtonsoft.Json.JsonPropertyAttribute("dataBits")] + [System.ComponentModel.DescriptionAttribute("Number of data bits per serial frame.")] + public int DataBits { get { - return _contrast; + return _dataBits; } set { - _contrast = value; + _dataBits = value; } } - public System.IObservable Generate() + /// + /// Number of stop bits per serial frame. + /// + [Newtonsoft.Json.JsonPropertyAttribute("stopBits")] + [System.ComponentModel.DescriptionAttribute("Number of stop bits per serial frame.")] + public string StopBits { - return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new Screen(this))); + get + { + return _stopBits; + } + set + { + _stopBits = value; + } } - public System.IObservable Generate(System.IObservable source) + /// + /// Hardware or software handshaking mode. + /// + [Newtonsoft.Json.JsonPropertyAttribute("handshake")] + [System.ComponentModel.DescriptionAttribute("Hardware or software handshaking mode.")] + public string Handshake { - return System.Reactive.Linq.Observable.Select(source, _ => new Screen(this)); + get + { + return _handshake; + } + set + { + _handshake = value; + } + } + + /// + /// Whether to discard null bytes appearing in the serial stream. + /// + [Newtonsoft.Json.JsonPropertyAttribute("discardNull")] + [System.ComponentModel.DescriptionAttribute("Whether to discard null bytes appearing in the serial stream.")] + public bool DiscardNull + { + get + { + return _discardNull; + } + set + { + _discardNull = value; + } + } + + /// + /// Whether to enable Data Terminal Ready (DTR) control line. + /// + [Newtonsoft.Json.JsonPropertyAttribute("dtrEnable")] + [System.ComponentModel.DescriptionAttribute("Whether to enable Data Terminal Ready (DTR) control line.")] + public bool DtrEnable + { + get + { + return _dtrEnable; + } + set + { + _dtrEnable = value; + } + } + + /// + /// Whether to enable Request To Send (RTS) control line. + /// + [Newtonsoft.Json.JsonPropertyAttribute("rtsEnable")] + [System.ComponentModel.DescriptionAttribute("Whether to enable Request To Send (RTS) control line.")] + public bool RtsEnable + { + get + { + return _rtsEnable; + } + set + { + _rtsEnable = value; + } + } + + /// + /// Size, in bytes, of the read buffer. + /// + [Newtonsoft.Json.JsonPropertyAttribute("readBufferSize")] + [System.ComponentModel.DescriptionAttribute("Size, in bytes, of the read buffer.")] + public int ReadBufferSize + { + get + { + return _readBufferSize; + } + set + { + _readBufferSize = value; + } + } + + /// + /// Size, in bytes, of the write buffer. + /// + [Newtonsoft.Json.JsonPropertyAttribute("writeBufferSize")] + [System.ComponentModel.DescriptionAttribute("Size, in bytes, of the write buffer.")] + public int WriteBufferSize + { + get + { + return _writeBufferSize; + } + set + { + _writeBufferSize = value; + } + } + + /// + /// Minimum number of bytes in the buffer that triggers a read event. + /// + [Newtonsoft.Json.JsonPropertyAttribute("receivedBytesThreshold")] + [System.ComponentModel.DescriptionAttribute("Minimum number of bytes in the buffer that triggers a read event.")] + public int ReceivedBytesThreshold + { + get + { + return _receivedBytesThreshold; + } + set + { + _receivedBytesThreshold = value; + } + } + + /// + /// Name of the subject to which parsed serial messages are published. + /// + [Newtonsoft.Json.JsonPropertyAttribute("serialMessageSubjectName")] + [System.ComponentModel.DescriptionAttribute("Name of the subject to which parsed serial messages are published.")] + public string SerialMessageSubjectName + { + get + { + return _serialMessageSubjectName; + } + set + { + _serialMessageSubjectName = value; + } + } + + public System.IObservable Generate() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new MatrixArduino(this))); + } + + public System.IObservable Generate(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new MatrixArduino(this)); } protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) { stringBuilder.Append("DeviceType = " + _deviceType + ", "); - stringBuilder.Append("DisplayIndex = " + _displayIndex + ", "); - stringBuilder.Append("TargetRenderFrequency = " + _targetRenderFrequency + ", "); - stringBuilder.Append("TargetUpdateFrequency = " + _targetUpdateFrequency + ", "); - stringBuilder.Append("TextureAssetsDirectory = " + _textureAssetsDirectory + ", "); - stringBuilder.Append("Calibration = " + _calibration + ", "); - stringBuilder.Append("Brightness = " + _brightness + ", "); - stringBuilder.Append("Contrast = " + _contrast); + stringBuilder.Append("PortName = " + _portName + ", "); + stringBuilder.Append("BaudRate = " + _baudRate + ", "); + stringBuilder.Append("Encoding = " + _encoding + ", "); + stringBuilder.Append("NewLine = " + _newLine + ", "); + stringBuilder.Append("Parity = " + _parity + ", "); + stringBuilder.Append("ParityReplace = " + _parityReplace + ", "); + stringBuilder.Append("DataBits = " + _dataBits + ", "); + stringBuilder.Append("StopBits = " + _stopBits + ", "); + stringBuilder.Append("Handshake = " + _handshake + ", "); + stringBuilder.Append("DiscardNull = " + _discardNull + ", "); + stringBuilder.Append("DtrEnable = " + _dtrEnable + ", "); + stringBuilder.Append("RtsEnable = " + _rtsEnable + ", "); + stringBuilder.Append("ReadBufferSize = " + _readBufferSize + ", "); + stringBuilder.Append("WriteBufferSize = " + _writeBufferSize + ", "); + stringBuilder.Append("ReceivedBytesThreshold = " + _receivedBytesThreshold + ", "); + stringBuilder.Append("SerialMessageSubjectName = " + _serialMessageSubjectName); return true; } @@ -652,432 +844,354 @@ public override string ToString() } - /// - /// Represents the SerialDevice workflow module. - /// - ///Mirrors all externalized properties of SerialDevice.bonsai, including - ///port configuration, framing, buffer settings, and parsing pattern. - /// [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] - [System.ComponentModel.DescriptionAttribute("Represents the SerialDevice workflow module.\n\nMirrors all externalized properties" + - " of SerialDevice.bonsai, including\nport configuration, framing, buffer settings," + - " and parsing pattern.")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] - public partial class SerialDeviceModule + public partial class MatrixArduinoData { - private string _deviceType; - - private string _portName; - - private int _baudRate; - - private string _pattern; - - private string _encoding; - - private string _newLine; - - private string _parity; - - private int _parityReplace; - - private int _dataBits; - - private string _stopBits; - - private string _handshake; + private int _encoderCount; - private bool _discardNull; - - private bool _dtrEnable; + private int _lickCountLeft; - private bool _rtsEnable; + private int _lickCountRight; - private int _readBufferSize; - - private int _writeBufferSize; + private int _lastSyncPulseTime; - private int _receivedBytesThreshold; + private int _photodiodeVal; - private string _serialMessageSubjectName; + private int _currentMs; - public SerialDeviceModule() + public MatrixArduinoData() { - _deviceType = "SerialDevice"; - _baudRate = 9600; - _pattern = ""; - _newLine = "\r\n"; - _parity = "None"; - _parityReplace = 63; - _dataBits = 8; - _stopBits = "One"; - _handshake = "None"; - _discardNull = false; - _dtrEnable = false; - _rtsEnable = false; - _readBufferSize = 4096; - _writeBufferSize = 2048; - _receivedBytesThreshold = 1; - _serialMessageSubjectName = "SerialMessages"; } - protected SerialDeviceModule(SerialDeviceModule other) + protected MatrixArduinoData(MatrixArduinoData other) { - _deviceType = other._deviceType; - _portName = other._portName; - _baudRate = other._baudRate; - _pattern = other._pattern; - _encoding = other._encoding; - _newLine = other._newLine; - _parity = other._parity; - _parityReplace = other._parityReplace; - _dataBits = other._dataBits; - _stopBits = other._stopBits; - _handshake = other._handshake; - _discardNull = other._discardNull; - _dtrEnable = other._dtrEnable; - _rtsEnable = other._rtsEnable; - _readBufferSize = other._readBufferSize; - _writeBufferSize = other._writeBufferSize; - _receivedBytesThreshold = other._receivedBytesThreshold; - _serialMessageSubjectName = other._serialMessageSubjectName; + _encoderCount = other._encoderCount; + _lickCountLeft = other._lickCountLeft; + _lickCountRight = other._lickCountRight; + _lastSyncPulseTime = other._lastSyncPulseTime; + _photodiodeVal = other._photodiodeVal; + _currentMs = other._currentMs; } - [Newtonsoft.Json.JsonPropertyAttribute("deviceType")] - public string DeviceType - { - get - { - return _deviceType; - } - set - { - _deviceType = value; - } - } - - /// - /// The name of the device serial port. - /// - [Newtonsoft.Json.JsonPropertyAttribute("portName", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("The name of the device serial port.")] - public string PortName - { - get - { - return _portName; - } - set - { - _portName = value; - } - } - - /// - /// Baud rate for serial communication. - /// - [Newtonsoft.Json.JsonPropertyAttribute("baudRate")] - [System.ComponentModel.DescriptionAttribute("Baud rate for serial communication.")] - public int BaudRate + [Newtonsoft.Json.JsonPropertyAttribute("encoderCount", Required=Newtonsoft.Json.Required.Always)] + public int EncoderCount { get { - return _baudRate; + return _encoderCount; } set { - _baudRate = value; + _encoderCount = value; } } - /// - /// Pattern used to parse each incoming serial line (same syntax as Bonsai Parse/ScanPattern). - /// - [Newtonsoft.Json.JsonPropertyAttribute("pattern")] - [System.ComponentModel.DescriptionAttribute("Pattern used to parse each incoming serial line (same syntax as Bonsai Parse/Scan" + - "Pattern).")] - public string Pattern + [Newtonsoft.Json.JsonPropertyAttribute("lickCountLeft", Required=Newtonsoft.Json.Required.Always)] + public int LickCountLeft { get { - return _pattern; + return _lickCountLeft; } set { - _pattern = value; + _lickCountLeft = value; } } - /// - /// Optional text encoding for interpreting incoming bytes. - /// - [Newtonsoft.Json.JsonPropertyAttribute("encoding")] - [System.ComponentModel.DescriptionAttribute("Optional text encoding for interpreting incoming bytes.")] - public string Encoding + [Newtonsoft.Json.JsonPropertyAttribute("lickCountRight", Required=Newtonsoft.Json.Required.Always)] + public int LickCountRight { get { - return _encoding; + return _lickCountRight; } set { - _encoding = value; + _lickCountRight = value; } } - /// - /// Line termination sequence used to delimit incoming messages. - /// - [Newtonsoft.Json.JsonPropertyAttribute("newLine")] - [System.ComponentModel.DescriptionAttribute("Line termination sequence used to delimit incoming messages.")] - public string NewLine + [Newtonsoft.Json.JsonPropertyAttribute("lastSyncPulseTime", Required=Newtonsoft.Json.Required.Always)] + public int LastSyncPulseTime { get { - return _newLine; + return _lastSyncPulseTime; } set { - _newLine = value; + _lastSyncPulseTime = value; } } - /// - /// Parity checking mode for the serial port. - /// - [Newtonsoft.Json.JsonPropertyAttribute("parity")] - [System.ComponentModel.DescriptionAttribute("Parity checking mode for the serial port.")] - public string Parity + [Newtonsoft.Json.JsonPropertyAttribute("photodiodeVal", Required=Newtonsoft.Json.Required.Always)] + public int PhotodiodeVal { get { - return _parity; + return _photodiodeVal; } set { - _parity = value; + _photodiodeVal = value; } } - /// - /// Byte used to replace invalid bytes detected by a parity error. - /// - [Newtonsoft.Json.JsonPropertyAttribute("parityReplace")] - [System.ComponentModel.DescriptionAttribute("Byte used to replace invalid bytes detected by a parity error.")] - public int ParityReplace + [Newtonsoft.Json.JsonPropertyAttribute("currentMs", Required=Newtonsoft.Json.Required.Always)] + public int CurrentMs { get { - return _parityReplace; + return _currentMs; } set { - _parityReplace = value; + _currentMs = value; } } - /// - /// Number of data bits per serial frame. - /// - [Newtonsoft.Json.JsonPropertyAttribute("dataBits")] - [System.ComponentModel.DescriptionAttribute("Number of data bits per serial frame.")] - public int DataBits + public System.IObservable Generate() { - get - { - return _dataBits; - } - set - { - _dataBits = value; - } + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new MatrixArduinoData(this))); } - /// - /// Number of stop bits per serial frame. - /// - [Newtonsoft.Json.JsonPropertyAttribute("stopBits")] - [System.ComponentModel.DescriptionAttribute("Number of stop bits per serial frame.")] - public string StopBits + public System.IObservable Generate(System.IObservable source) { - get - { - return _stopBits; - } - set + return System.Reactive.Linq.Observable.Select(source, _ => new MatrixArduinoData(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("EncoderCount = " + _encoderCount + ", "); + stringBuilder.Append("LickCountLeft = " + _lickCountLeft + ", "); + stringBuilder.Append("LickCountRight = " + _lickCountRight + ", "); + stringBuilder.Append("LastSyncPulseTime = " + _lastSyncPulseTime + ", "); + stringBuilder.Append("PhotodiodeVal = " + _photodiodeVal + ", "); + stringBuilder.Append("CurrentMs = " + _currentMs); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) { - _stopBits = value; + stringBuilder.Append(" "); } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + [Bonsai.CombinatorAttribute(MethodName="Generate")] + public partial class Screen + { + + private string _deviceType; + + private int _displayIndex; + + private double _targetRenderFrequency; + + private double _targetUpdateFrequency; + + private string _textureAssetsDirectory; + + private System.Collections.Generic.Dictionary _calibration; + + private double _brightness; + + private double _contrast; + + public Screen() + { + _deviceType = "Screen"; + _displayIndex = 1; + _targetRenderFrequency = 60D; + _targetUpdateFrequency = 120D; + _textureAssetsDirectory = "Textures"; + _brightness = 0D; + _contrast = 1D; + } + + protected Screen(Screen other) + { + _deviceType = other._deviceType; + _displayIndex = other._displayIndex; + _targetRenderFrequency = other._targetRenderFrequency; + _targetUpdateFrequency = other._targetUpdateFrequency; + _textureAssetsDirectory = other._textureAssetsDirectory; + _calibration = other._calibration; + _brightness = other._brightness; + _contrast = other._contrast; } /// - /// Hardware or software handshaking mode. + /// Device type /// - [Newtonsoft.Json.JsonPropertyAttribute("handshake")] - [System.ComponentModel.DescriptionAttribute("Hardware or software handshaking mode.")] - public string Handshake + [Newtonsoft.Json.JsonPropertyAttribute("deviceType")] + [System.ComponentModel.DescriptionAttribute("Device type")] + public string DeviceType { get { - return _handshake; + return _deviceType; } set { - _handshake = value; + _deviceType = value; } } /// - /// Whether to discard null bytes appearing in the serial stream. + /// Display index /// - [Newtonsoft.Json.JsonPropertyAttribute("discardNull")] - [System.ComponentModel.DescriptionAttribute("Whether to discard null bytes appearing in the serial stream.")] - public bool DiscardNull + [Newtonsoft.Json.JsonPropertyAttribute("displayIndex")] + [System.ComponentModel.DescriptionAttribute("Display index")] + public int DisplayIndex { get { - return _discardNull; + return _displayIndex; } set { - _discardNull = value; + _displayIndex = value; } } /// - /// Whether to enable Data Terminal Ready (DTR) control line. + /// Target render frequency /// - [Newtonsoft.Json.JsonPropertyAttribute("dtrEnable")] - [System.ComponentModel.DescriptionAttribute("Whether to enable Data Terminal Ready (DTR) control line.")] - public bool DtrEnable + [Newtonsoft.Json.JsonPropertyAttribute("targetRenderFrequency")] + [System.ComponentModel.DescriptionAttribute("Target render frequency")] + public double TargetRenderFrequency { get { - return _dtrEnable; + return _targetRenderFrequency; } set { - _dtrEnable = value; + _targetRenderFrequency = value; } } /// - /// Whether to enable Request To Send (RTS) control line. + /// Target update frequency /// - [Newtonsoft.Json.JsonPropertyAttribute("rtsEnable")] - [System.ComponentModel.DescriptionAttribute("Whether to enable Request To Send (RTS) control line.")] - public bool RtsEnable + [Newtonsoft.Json.JsonPropertyAttribute("targetUpdateFrequency")] + [System.ComponentModel.DescriptionAttribute("Target update frequency")] + public double TargetUpdateFrequency { get { - return _rtsEnable; + return _targetUpdateFrequency; } set { - _rtsEnable = value; + _targetUpdateFrequency = value; } } /// - /// Size, in bytes, of the read buffer. + /// Calibration directory /// - [Newtonsoft.Json.JsonPropertyAttribute("readBufferSize")] - [System.ComponentModel.DescriptionAttribute("Size, in bytes, of the read buffer.")] - public int ReadBufferSize + [Newtonsoft.Json.JsonPropertyAttribute("textureAssetsDirectory")] + [System.ComponentModel.DescriptionAttribute("Calibration directory")] + public string TextureAssetsDirectory { get { - return _readBufferSize; + return _textureAssetsDirectory; } set { - _readBufferSize = value; + _textureAssetsDirectory = value; } } /// - /// Size, in bytes, of the write buffer. + /// Calibration parameters for a set of named display monitors for visual stimuli /// - [Newtonsoft.Json.JsonPropertyAttribute("writeBufferSize")] - [System.ComponentModel.DescriptionAttribute("Size, in bytes, of the write buffer.")] - public int WriteBufferSize + [System.Xml.Serialization.XmlIgnoreAttribute()] + [Newtonsoft.Json.JsonPropertyAttribute("calibration")] + [System.ComponentModel.DescriptionAttribute("Calibration parameters for a set of named display monitors for visual stimuli")] + public System.Collections.Generic.Dictionary Calibration { get { - return _writeBufferSize; + return _calibration; } set { - _writeBufferSize = value; + _calibration = value; } } /// - /// Minimum number of bytes in the buffer that triggers a read event. + /// Brightness /// - [Newtonsoft.Json.JsonPropertyAttribute("receivedBytesThreshold")] - [System.ComponentModel.DescriptionAttribute("Minimum number of bytes in the buffer that triggers a read event.")] - public int ReceivedBytesThreshold + [Newtonsoft.Json.JsonPropertyAttribute("brightness")] + [System.ComponentModel.DescriptionAttribute("Brightness")] + public double Brightness { get { - return _receivedBytesThreshold; + return _brightness; } set { - _receivedBytesThreshold = value; + _brightness = value; } } /// - /// Name of the subject to which parsed serial messages are published. + /// Contrast /// - [Newtonsoft.Json.JsonPropertyAttribute("serialMessageSubjectName")] - [System.ComponentModel.DescriptionAttribute("Name of the subject to which parsed serial messages are published.")] - public string SerialMessageSubjectName + [Newtonsoft.Json.JsonPropertyAttribute("contrast")] + [System.ComponentModel.DescriptionAttribute("Contrast")] + public double Contrast { get { - return _serialMessageSubjectName; + return _contrast; } set { - _serialMessageSubjectName = value; + _contrast = value; } } - public System.IObservable Generate() + public System.IObservable Generate() { - return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new SerialDeviceModule(this))); + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new Screen(this))); } - public System.IObservable Generate(System.IObservable source) + public System.IObservable Generate(System.IObservable source) { - return System.Reactive.Linq.Observable.Select(source, _ => new SerialDeviceModule(this)); + return System.Reactive.Linq.Observable.Select(source, _ => new Screen(this)); } protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) { stringBuilder.Append("DeviceType = " + _deviceType + ", "); - stringBuilder.Append("PortName = " + _portName + ", "); - stringBuilder.Append("BaudRate = " + _baudRate + ", "); - stringBuilder.Append("Pattern = " + _pattern + ", "); - stringBuilder.Append("Encoding = " + _encoding + ", "); - stringBuilder.Append("NewLine = " + _newLine + ", "); - stringBuilder.Append("Parity = " + _parity + ", "); - stringBuilder.Append("ParityReplace = " + _parityReplace + ", "); - stringBuilder.Append("DataBits = " + _dataBits + ", "); - stringBuilder.Append("StopBits = " + _stopBits + ", "); - stringBuilder.Append("Handshake = " + _handshake + ", "); - stringBuilder.Append("DiscardNull = " + _discardNull + ", "); - stringBuilder.Append("DtrEnable = " + _dtrEnable + ", "); - stringBuilder.Append("RtsEnable = " + _rtsEnable + ", "); - stringBuilder.Append("ReadBufferSize = " + _readBufferSize + ", "); - stringBuilder.Append("WriteBufferSize = " + _writeBufferSize + ", "); - stringBuilder.Append("ReceivedBytesThreshold = " + _receivedBytesThreshold + ", "); - stringBuilder.Append("SerialMessageSubjectName = " + _serialMessageSubjectName); + stringBuilder.Append("DisplayIndex = " + _displayIndex + ", "); + stringBuilder.Append("TargetRenderFrequency = " + _targetRenderFrequency + ", "); + stringBuilder.Append("TargetUpdateFrequency = " + _targetUpdateFrequency + ", "); + stringBuilder.Append("TextureAssetsDirectory = " + _textureAssetsDirectory + ", "); + stringBuilder.Append("Calibration = " + _calibration + ", "); + stringBuilder.Append("Brightness = " + _brightness + ", "); + stringBuilder.Append("Contrast = " + _contrast); return true; } @@ -1106,13 +1220,13 @@ public partial class UclOpenHfVisualRig private Screen _screen; - private SerialDeviceModule _arduino; + private MatrixArduino _arduino; public UclOpenHfVisualRig() { _version = "0.0.0-rc1"; _screen = new Screen(); - _arduino = new SerialDeviceModule(); + _arduino = new MatrixArduino(); } protected UclOpenHfVisualRig(UclOpenHfVisualRig other) @@ -1151,7 +1265,7 @@ public Screen Screen [System.Xml.Serialization.XmlIgnoreAttribute()] [Newtonsoft.Json.JsonPropertyAttribute("arduino", Required=Newtonsoft.Json.Required.Always)] - public SerialDeviceModule Arduino + public MatrixArduino Arduino { get { @@ -1463,10 +1577,10 @@ protected Vector3(Vector3 other) } /// - /// X coordinate of the point + /// X coordinate of the point. /// [Newtonsoft.Json.JsonPropertyAttribute("x", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("X coordinate of the point")] + [System.ComponentModel.DescriptionAttribute("X coordinate of the point.")] public double X { get @@ -1480,10 +1594,10 @@ public double X } /// - /// Y coordinate of the point + /// Y coordinate of the point. /// [Newtonsoft.Json.JsonPropertyAttribute("y", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("Y coordinate of the point")] + [System.ComponentModel.DescriptionAttribute("Y coordinate of the point.")] public double Y { get @@ -1497,10 +1611,10 @@ public double Y } /// - /// Z coordinate of the point + /// Z coordinate of the point. /// [Newtonsoft.Json.JsonPropertyAttribute("z", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("Z coordinate of the point")] + [System.ComponentModel.DescriptionAttribute("Z coordinate of the point.")] public double Z { get @@ -1584,14 +1698,19 @@ public System.IObservable Process(System.IObservable source) return Process(source); } - public System.IObservable Process(System.IObservable source) + public System.IObservable Process(System.IObservable source) { - return Process(source); + return Process(source); + } + + public System.IObservable Process(System.IObservable source) + { + return Process(source); } - public System.IObservable Process(System.IObservable source) + public System.IObservable Process(System.IObservable source) { - return Process(source); + return Process(source); } public System.IObservable Process(System.IObservable source) @@ -1627,8 +1746,9 @@ public System.IObservable Process(System.IObservable source) [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] - [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] diff --git a/src/main.bonsai b/src/main.bonsai index be11309..ef02b17 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -8,8 +8,8 @@ xmlns:res="clr-namespace:Bonsai.Resources;assembly=Bonsai.System" xmlns:num="clr-namespace:Bonsai.Numerics;assembly=Bonsai.Numerics" xmlns:scr="clr-namespace:Bonsai.Scripting.Expressions;assembly=Bonsai.Scripting.Expressions" - xmlns:harp="clr-namespace:Bonsai.Harp;assembly=Bonsai.Harp" - xmlns:p2="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" + xmlns:p2="clr-namespace:;assembly=Extensions" + xmlns:p3="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" xmlns="https://bonsai-rx.org/2018/workflow"> @@ -153,7 +153,6 @@ - @@ -174,8 +173,6 @@ 2048 1 Arduino - %d - , SerialMessages @@ -424,7 +421,7 @@ - SN_1_1 + SynthIm_1_1 @@ -557,33 +554,14 @@ SerialMessages - - new( - it[0] as EncoderCount, - it[1] as LickCountLeft, - it[2] as LickCountRight, - it[3] as LastSyncPulseTime, - it[4] as PhotodiodeVal, - it[5] as ArduinoMilliseconds -) - - - ArduinoMilliseconds - - - - 1000 - - - + - - - - - TODO - + + + TODO + + LogData @@ -621,7 +599,7 @@ - + @@ -716,13 +694,8 @@ - - - - - - - + + \ No newline at end of file diff --git a/src/ucl_open_hf_visual/data_types.py b/src/ucl_open_hf_visual/data_types.py new file mode 100644 index 0000000..06933f7 --- /dev/null +++ b/src/ucl_open_hf_visual/data_types.py @@ -0,0 +1,9 @@ +from ucl_open.rigs.base import BaseSchema + +class MatrixArduinoData(BaseSchema): + encoder_count: int + lick_count_left: int + lick_count_right: int + last_sync_pulse_time: int + photodiode_val: int + current_ms: int \ No newline at end of file diff --git a/src/ucl_open_hf_visual/regenerate.py b/src/ucl_open_hf_visual/regenerate.py index 7099b92..e52e27a 100644 --- a/src/ucl_open_hf_visual/regenerate.py +++ b/src/ucl_open_hf_visual/regenerate.py @@ -7,6 +7,7 @@ import ucl_open_hf_visual.rig import ucl_open_hf_visual.task +import ucl_open_hf_visual.data_types SCHEMA_ROOT = Path("./src/DataSchemas/") EXTENSIONS_ROOT = Path("./src/Extensions/") @@ -18,6 +19,7 @@ def main(): ucl_open_hf_visual.task.UclOpenHfVisualTaskLogic, ucl_open_hf_visual.rig.UclOpenHfVisualRig, Experiment, + ucl_open_hf_visual.data_types.MatrixArduinoData ] model = pydantic.RootModel[Union[tuple(models)]] diff --git a/src/ucl_open_hf_visual/rig.py b/src/ucl_open_hf_visual/rig.py index 5398565..072bb7c 100644 --- a/src/ucl_open_hf_visual/rig.py +++ b/src/ucl_open_hf_visual/rig.py @@ -8,9 +8,11 @@ from ucl_open_hf_visual import __semver__ +class MatrixArduino(SerialDeviceModule): + device_type: Literal["MatrixArduino"] = "MatrixArduino" class UclOpenHfVisualRig(BaseSchema): version: Literal[__semver__] = __semver__ # type: ignore screen: Screen - arduino: SerialDeviceModule + arduino: MatrixArduino ... \ No newline at end of file diff --git a/src/ucl_open_hf_visual/visual_stimulation.py b/src/ucl_open_hf_visual/visual_stimulation.py index cb138ef..a8c9945 100644 --- a/src/ucl_open_hf_visual/visual_stimulation.py +++ b/src/ucl_open_hf_visual/visual_stimulation.py @@ -4,7 +4,7 @@ from typing import Literal from pydantic import Field -from swc.aeon.schema import BaseSchema, Device +from ucl_open.rigs.base import BaseSchema, Device class Vector3(BaseSchema): x: float = Field(default=0, description="X coordinate of the point") diff --git a/uv.lock b/uv.lock index 18f5a83..8894d56 100644 --- a/uv.lock +++ b/uv.lock @@ -775,7 +775,7 @@ requires-dist = [ { name = "contraqctor", marker = "extra == 'data'", specifier = "<0.5.0" }, { name = "pydantic-settings" }, { name = "swc-aeon", specifier = ">=0.1.0" }, - { name = "ucl-open-rigs", git = "https://github.com/ucl-open/ucl_open_rigs/?branch=visual-displays" }, + { name = "ucl-open-rigs", git = "https://github.com/ucl-open/ucl_open_rigs/?branch=test_build" }, ] provides-extras = ["data"] @@ -790,7 +790,7 @@ dev = [ [[package]] name = "ucl-open-rigs" version = "0.1.0" -source = { git = "https://github.com/ucl-open/ucl_open_rigs/?branch=visual-displays#1bdf5495e28d3dc764657a146ee020625ea7daa1" } +source = { git = "https://github.com/ucl-open/ucl_open_rigs/?branch=test_build#5cabc5b4d596747a60cf74431f5b76c4bdbfe019" } dependencies = [ { name = "pydantic" }, { name = "pydantic-yaml" }, From 245ecfb3fc0c0659ad6da71124ac271c1f14d846 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 13:38:17 +0000 Subject: [PATCH 02/17] Add blank textures --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 8 ++++-- examples/task.py | 2 +- src/main.bonsai | 48 ++++++++++++++++++++----------- textures/blankStim_1.tif | Bin 0 -> 4550 bytes textures/blankStim_2.tif | Bin 0 -> 4550 bytes 6 files changed, 39 insertions(+), 21 deletions(-) create mode 100644 textures/blankStim_1.tif create mode 100644 textures/blankStim_2.tif diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 144fd97..998d34c 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index 9f0e399..bbe8b83 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -3,6 +3,7 @@ + false 344 310 @@ -34,6 +35,7 @@ + false 511 236 @@ -48,6 +50,7 @@ + false 500 144 @@ -65,7 +68,7 @@ - + false 640 @@ -80,7 +83,8 @@ - + + false 174 146 diff --git a/examples/task.py b/examples/task.py index d396aba..cb12854 100644 --- a/examples/task.py +++ b/examples/task.py @@ -8,7 +8,7 @@ task_logic = UclOpenHfVisualTaskLogic( task_parameters=UclOpenHfVisualTaskParameters( available_textures=["SN_1_1", "SynthIm_1_1"], - inter_presentation_texture="SN_1_2", + inter_presentation_texture="blankStim_1", presentation_time=1, inter_presentation_time=0.5 ), diff --git a/src/main.bonsai b/src/main.bonsai index ef02b17..af4dff9 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -199,7 +199,7 @@ true Resizable - Fullscreen + Normal Second 60 120 @@ -375,6 +375,21 @@ 0 0 + + DrawCall + + + 0.1 + 0.1 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + TaskLogicParameters @@ -421,7 +436,7 @@ - SynthIm_1_1 + SN_1_1 @@ -473,7 +488,7 @@ - SN_1_2 + blankStim_1 @@ -557,11 +572,9 @@ - - - TODO - - + + TODO + LogData @@ -648,7 +661,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-10T17-50-17\Arduino\Arduino.csv + C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T13-36-07\Arduino\Arduino.csv false false None @@ -686,16 +699,17 @@ - - - - - + + + + - - - + + + + + \ No newline at end of file diff --git a/textures/blankStim_1.tif b/textures/blankStim_1.tif new file mode 100644 index 0000000000000000000000000000000000000000..0979dc9e4c8b8553b195df1825cbe5d529e9e5cb GIT binary patch literal 4550 zcmeI0OA5j;6h&X`$Lrq;DyRd8;zGpks?M?^hYnqrrzJvmAjoSuy|L73&j#D4=ii(hLN(0$q#sj%(NEdfOX@6b*zO$rdB54we@X8qf>=}p pvCs%ANKi?FDiTzapfL&J1ranMK~oYmBSCW#v>-uzt_WI@@B#m@D7gRt literal 0 HcmV?d00001 diff --git a/textures/blankStim_2.tif b/textures/blankStim_2.tif new file mode 100644 index 0000000000000000000000000000000000000000..0979dc9e4c8b8553b195df1825cbe5d529e9e5cb GIT binary patch literal 4550 zcmeI0OA5j;6h&X`$Lrq;DyRd8;zGpks?M?^hYnqrrzJvmAjoSuy|L73&j#D4=ii(hLN(0$q#sj%(NEdfOX@6b*zO$rdB54we@X8qf>=}p pvCs%ANKi?FDiTzapfL&J1ranMK~oYmBSCW#v>-uzt_WI@@B#m@D7gRt literal 0 HcmV?d00001 From 1983476f15845e75850c8b5e0208fd748711b9a5 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 13:42:37 +0000 Subject: [PATCH 03/17] Reverse order of blank and texture presentation --- .bonsai/Settings/src/main.layout | 4 +- src/main.bonsai | 71 ++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index bbe8b83..d327e18 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -68,7 +68,7 @@ - + false 640 @@ -83,7 +83,7 @@ - + false 174 diff --git a/src/main.bonsai b/src/main.bonsai index af4dff9..d7cd4a1 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -390,6 +390,14 @@ 1 1 + + + false + + + + SynchQuadColor + TaskLogicParameters @@ -427,7 +435,10 @@ TextureBank - TextureName + TaskLogicParameters + + + InterPresentationTexture @@ -436,7 +447,7 @@ - SN_1_1 + blankStim_1 @@ -451,7 +462,7 @@ TaskLogicParameters - PresentationTime + InterPresentationTime TimeSpan.FromSeconds(it) @@ -463,7 +474,7 @@ - PT1S + PT0.5S @@ -476,10 +487,7 @@ Item2 - TaskLogicParameters - - - InterPresentationTexture + TextureName @@ -488,7 +496,7 @@ - blankStim_1 + SynthIm_1_1 @@ -503,7 +511,7 @@ TaskLogicParameters - InterPresentationTime + PresentationTime TimeSpan.FromSeconds(it) @@ -515,7 +523,7 @@ - PT0.5S + PT1S @@ -528,20 +536,20 @@ - + - - - - + + + + - - - - - - + + + + + + @@ -661,7 +669,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T13-36-07\Arduino\Arduino.csv + C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T13-42-17\Arduino\Arduino.csv false false None @@ -700,16 +708,17 @@ - - - - - + + + + - - - + + + + + \ No newline at end of file From e2a9a116550c90b9a678a488ca8f281ac001b7ec Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 13:50:05 +0000 Subject: [PATCH 04/17] Add sync quad --- .bonsai/Settings/src/main.layout | 6 +- src/main.bonsai | 146 ++++++++++++++++++++++++------- 2 files changed, 118 insertions(+), 34 deletions(-) diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index d327e18..ee920a0 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -1,6 +1,6 @@  - + false @@ -68,7 +68,7 @@ - + false 640 @@ -83,7 +83,7 @@ - + false 174 diff --git a/src/main.bonsai b/src/main.bonsai index d7cd4a1..8ba3530 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -6,8 +6,8 @@ xmlns:p1="clr-namespace:UclOpenHfVisualDataSchema;assembly=Extensions" xmlns:gl="clr-namespace:Bonsai.Shaders;assembly=Bonsai.Shaders" xmlns:res="clr-namespace:Bonsai.Resources;assembly=Bonsai.System" - xmlns:num="clr-namespace:Bonsai.Numerics;assembly=Bonsai.Numerics" xmlns:scr="clr-namespace:Bonsai.Scripting.Expressions;assembly=Bonsai.Scripting.Expressions" + xmlns:num="clr-namespace:Bonsai.Numerics;assembly=Bonsai.Numerics" xmlns:p2="clr-namespace:;assembly=Extensions" xmlns:p3="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" xmlns="https://bonsai-rx.org/2018/workflow"> @@ -18,6 +18,10 @@ + + TODO + + ReadSchemas @@ -378,6 +382,23 @@ DrawCall + + SyncQuadColor + + + it ? 1.0 : 0.0 + + + + + + + + + + + Item1 + 0.1 0.1 @@ -396,7 +417,7 @@ - SynchQuadColor + SyncQuadColor TaskLogicParameters @@ -458,6 +479,34 @@ Texture2D + + UpdateSyncQuad + + + + Source1 + + + + + + + false + + + + SyncQuadColor + + + + + + + + + + + TaskLogicParameters @@ -507,6 +556,34 @@ Texture2D + + UpdateSyncQuad + + + + Source1 + + + + + + + true + + + + SyncQuadColor + + + + + + + + + + + TaskLogicParameters @@ -541,25 +618,27 @@ - - + + - - - - - - - - - + + + + + + + + + - + - + - + + + @@ -669,7 +748,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T13-42-17\Arduino\Arduino.csv + C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T13-48-46\Arduino\Arduino.csv false false None @@ -690,35 +769,40 @@ - - + + - + - + - - - + + + - - + + + + - - - + + + - - + + + + + \ No newline at end of file From a64940d9377d53906bf719448a83eb69833de782 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 14:04:12 +0000 Subject: [PATCH 05/17] Clean up workflow --- .bonsai/Settings/src/main.layout | 110 +- examples/rig.py | 9 +- src/DataSchemas/ucl_open_hf_visual.json | 32 + src/Extensions/UclOpenHfVisual.Generated.cs | 137 +++ src/main.bonsai | 1136 ++++++++++--------- src/ucl_open_hf_visual/rig.py | 9 + 6 files changed, 839 insertions(+), 594 deletions(-) diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index ee920a0..bab0b8c 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -1,44 +1,14 @@  - + - - false - - 344 - 310 - - - 334 - 64 - - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 361 - 396 - - - 334 - 64 - - Bonsai.Design.ObjectTextVisualizer - - - - - + - + false - 511 - 236 + 344 + 310 334 @@ -49,11 +19,11 @@ - + false - 500 - 144 + 361 + 396 334 @@ -64,38 +34,42 @@ + + + + false + + 511 + 236 + + + 334 + 64 + + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 500 + 144 + + + 334 + 64 + + Bonsai.Design.ObjectTextVisualizer + + + + + + - - false - - 640 - 283 - - - 334 - 184 - - Bonsai.Design.ObjectTextVisualizer - - - - - - false - - 174 - 146 - - - 837 - 213 - - Bonsai.Design.ObjectTextVisualizer - - - - \ No newline at end of file diff --git a/examples/rig.py b/examples/rig.py index 6bbbc66..28dfe77 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -4,7 +4,8 @@ UclOpenHfVisualRig ) from ucl_open_hf_visual.rig import ( - MatrixArduino + MatrixArduino, + SyncQuad ) from ucl_open.rigs.device import ( Screen, @@ -14,6 +15,12 @@ screen=Screen( texture_assets_directory="../textures" ), + sync_quad=SyncQuad( + extent_x=0.1, + extent_y=0.1, + location_x=1, + location_y=1 + ), arduino=MatrixArduino( port_name="COM10", baud_rate=1000000, diff --git a/src/DataSchemas/ucl_open_hf_visual.json b/src/DataSchemas/ucl_open_hf_visual.json index 8049347..36306d2 100644 --- a/src/DataSchemas/ucl_open_hf_visual.json +++ b/src/DataSchemas/ucl_open_hf_visual.json @@ -348,6 +348,34 @@ "title": "Screen", "type": "object" }, + "SyncQuad": { + "properties": { + "extentX": { + "title": "ExtentX", + "type": "number" + }, + "extentY": { + "title": "ExtentY", + "type": "number" + }, + "locationX": { + "title": "LocationX", + "type": "number" + }, + "locationY": { + "title": "LocationY", + "type": "number" + } + }, + "required": [ + "extentX", + "extentY", + "locationX", + "locationY" + ], + "title": "SyncQuad", + "type": "object" + }, "UclOpenHfVisualRig": { "properties": { "version": { @@ -359,6 +387,9 @@ "screen": { "$ref": "#/$defs/Screen" }, + "syncQuad": { + "$ref": "#/$defs/SyncQuad" + }, "arduino": { "$ref": "#/$defs/MatrixArduino", "title": "Arduino" @@ -366,6 +397,7 @@ }, "required": [ "screen", + "syncQuad", "arduino" ], "title": "UclOpenHfVisualRig", diff --git a/src/Extensions/UclOpenHfVisual.Generated.cs b/src/Extensions/UclOpenHfVisual.Generated.cs index 6f418c9..6aec3f2 100644 --- a/src/Extensions/UclOpenHfVisual.Generated.cs +++ b/src/Extensions/UclOpenHfVisual.Generated.cs @@ -1210,6 +1210,118 @@ public override string ToString() } + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + [Bonsai.CombinatorAttribute(MethodName="Generate")] + public partial class SyncQuad + { + + private double _extentX; + + private double _extentY; + + private double _locationX; + + private double _locationY; + + public SyncQuad() + { + } + + protected SyncQuad(SyncQuad other) + { + _extentX = other._extentX; + _extentY = other._extentY; + _locationX = other._locationX; + _locationY = other._locationY; + } + + [Newtonsoft.Json.JsonPropertyAttribute("extentX", Required=Newtonsoft.Json.Required.Always)] + public double ExtentX + { + get + { + return _extentX; + } + set + { + _extentX = value; + } + } + + [Newtonsoft.Json.JsonPropertyAttribute("extentY", Required=Newtonsoft.Json.Required.Always)] + public double ExtentY + { + get + { + return _extentY; + } + set + { + _extentY = value; + } + } + + [Newtonsoft.Json.JsonPropertyAttribute("locationX", Required=Newtonsoft.Json.Required.Always)] + public double LocationX + { + get + { + return _locationX; + } + set + { + _locationX = value; + } + } + + [Newtonsoft.Json.JsonPropertyAttribute("locationY", Required=Newtonsoft.Json.Required.Always)] + public double LocationY + { + get + { + return _locationY; + } + set + { + _locationY = value; + } + } + + public System.IObservable Generate() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new SyncQuad(this))); + } + + public System.IObservable Generate(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new SyncQuad(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("ExtentX = " + _extentX + ", "); + stringBuilder.Append("ExtentY = " + _extentY + ", "); + stringBuilder.Append("LocationX = " + _locationX + ", "); + stringBuilder.Append("LocationY = " + _locationY); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] @@ -1220,12 +1332,15 @@ public partial class UclOpenHfVisualRig private Screen _screen; + private SyncQuad _syncQuad; + private MatrixArduino _arduino; public UclOpenHfVisualRig() { _version = "0.0.0-rc1"; _screen = new Screen(); + _syncQuad = new SyncQuad(); _arduino = new MatrixArduino(); } @@ -1233,6 +1348,7 @@ protected UclOpenHfVisualRig(UclOpenHfVisualRig other) { _version = other._version; _screen = other._screen; + _syncQuad = other._syncQuad; _arduino = other._arduino; } @@ -1263,6 +1379,20 @@ public Screen Screen } } + [System.Xml.Serialization.XmlIgnoreAttribute()] + [Newtonsoft.Json.JsonPropertyAttribute("syncQuad", Required=Newtonsoft.Json.Required.Always)] + public SyncQuad SyncQuad + { + get + { + return _syncQuad; + } + set + { + _syncQuad = value; + } + } + [System.Xml.Serialization.XmlIgnoreAttribute()] [Newtonsoft.Json.JsonPropertyAttribute("arduino", Required=Newtonsoft.Json.Required.Always)] public MatrixArduino Arduino @@ -1291,6 +1421,7 @@ protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) { stringBuilder.Append("Version = " + _version + ", "); stringBuilder.Append("Screen = " + _screen + ", "); + stringBuilder.Append("SyncQuad = " + _syncQuad + ", "); stringBuilder.Append("Arduino = " + _arduino); return true; } @@ -1713,6 +1844,11 @@ public System.IObservable Process(System.IObservable source) return Process(source); } + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + public System.IObservable Process(System.IObservable source) { return Process(source); @@ -1749,6 +1885,7 @@ public System.IObservable Process(System.IObservable source) [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] diff --git a/src/main.bonsai b/src/main.bonsai index 8ba3530..5c0da02 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -4,12 +4,13 @@ xmlns:io="clr-namespace:Bonsai.IO;assembly=Bonsai.System" xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core" xmlns:p1="clr-namespace:UclOpenHfVisualDataSchema;assembly=Extensions" + xmlns:p2="clr-namespace:System.Reactive;assembly=System.Reactive.Core" xmlns:gl="clr-namespace:Bonsai.Shaders;assembly=Bonsai.Shaders" xmlns:res="clr-namespace:Bonsai.Resources;assembly=Bonsai.System" xmlns:scr="clr-namespace:Bonsai.Scripting.Expressions;assembly=Bonsai.Scripting.Expressions" xmlns:num="clr-namespace:Bonsai.Numerics;assembly=Bonsai.Numerics" - xmlns:p2="clr-namespace:;assembly=Extensions" - xmlns:p3="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" + xmlns:p3="clr-namespace:;assembly=Extensions" + xmlns:p4="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" xmlns="https://bonsai-rx.org/2018/workflow"> @@ -146,624 +147,737 @@ - - RigSchema - - - Arduino - - - - - - - - - - COM10 - 1000000 - - - - None - 63 - 8 - One - None - false - false - false - 4096 - 2048 - 1 - Arduino - SerialMessages - - - RigSchema - - - Screen - - - - - - - - - - - 640 - 480 - On - false - Black - DepthBufferBit ColorBufferBit - true - - Resizable - Normal - Second - 60 - 120 - - - - 8 - 8 - 8 - 8 - - 16 - 0 - 0 - - 0 - 0 - 0 - 0 - - 2 - false - - - - - - - - - RigSchema - - - Screen.TextureAssetsDirectory - - - - - - - - TODO - + + Initialization + + + + StartExperiment + + + StopExperiment + + + + - LoadTextureBank + Hardware - - - + + RigSchema - - - ../textures - *.tif - TopDirectoryOnly - + + Arduino - - + + + + + + - - LoadTexture + + COM10 + 1000000 + + + + None + 63 + 8 + One + None + false + false + false + 4096 + 2048 + 1 + Arduino + SerialMessages + + + Display - - Source1 + + RigSchema + + + Screen + + + + + + + - - 1 + + 640 + 480 + On + false + Black + DepthBufferBit ColorBufferBit + true + + Resizable + Normal + Second + 60 + 120 + + + + 8 + 8 + 8 + 8 + + 16 + 0 + 0 + + 0 + 0 + 0 + 0 + + 2 + false + - - TexturePath + + + + + + RigSchema + + + Screen.TextureAssetsDirectory + + + + + + + + TODO + + + + LoadTextureBank + + + + + + + + + ../textures + *.tif + TopDirectoryOnly + + + + + + + LoadTexture + + + + Source1 + + + + 1 + + + + TexturePath + + + TexturePath + + + + + + TexturePath + + + + + + + + + + + Rgba + Repeat + Repeat + Linear + Linear + ../textures\SynthIm_42_5.tif + Unchanged + Vertical + + + + + + + + + + + + + + + + + + + + + + + + Item1 + Item2 + + + + + + TextureBank + + + + + + + + + + + + + + + + + + -1 + 1 + -1 + 1 + + + DrawCall - TexturePath + DrawCall + + + + 2 + 2 + 0 + 0 + 0 + 0 + 1 + 1 + 0 + 0 + + + DrawCall + + + SyncQuadColor + + + it ? 1.0 : 0.0 - + + + + + + + + + Item1 - TexturePath + RigSchema + + + SyncQuad - + + + + + + 0.1 + 0.1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + - - - - Rgba - Repeat - Repeat - Linear - Linear - ../textures\SynthIm_42_5.tif - Unchanged - Vertical + + false - - + + SyncQuadColor - + - - + - - + + + + + + + + + + + + + + + - - - - - Item1 - Item2 - - - - - - TextureBank - - - - - - - - - - -1 - 1 - -1 - 1 - - - DrawCall - - - DrawCall - - - - 2 - 2 - 0 - 0 - 0 - 0 - 1 - 1 - 0 - 0 - - - DrawCall - - - SyncQuadColor - - - it ? 1.0 : 0.0 - - - - - - - - - - - Item1 - - - 0.1 - 0.1 - 1 - 1 - 0 - 0 - 1 - 1 - 1 - 1 - - - - false - - - - SyncQuadColor - - - TaskLogicParameters - - - AvailableTextures - - - - - - - - - TextureBank - - - - - - PresentationSequence + + TaskLogic - - Source1 - - - - 1 - - - - TextureName - - - TextureBank - TaskLogicParameters - InterPresentationTexture + AvailableTextures - - - - + + + + + - - - blankStim_1 - + + TextureBank - - Texture0 - Image - - Texture2D - + - - UpdateSyncQuad + + PresentationSequence Source1 - - + + + 1 + + + + TextureName + + + TextureBank + + + TaskLogicParameters + + + InterPresentationTexture + + + + + + + + + blankStim_1 + - - false + + Texture0 + Image + + Texture2D - - SyncQuadColor + + UpdateSyncQuad + + + + Source1 + + + + + + + false + + + + SyncQuadColor + + + + + + + + + + - - - - - - - - - - - - TaskLogicParameters - - - InterPresentationTime - - - TimeSpan.FromSeconds(it) - - - - - - - - - PT0.5S - - - - TextureBank - - - - - - Item2 - - - TextureName - - - - - - - - - SynthIm_1_1 - - - - - Texture0 - Image - - Texture2D - - - - UpdateSyncQuad - - - - Source1 + + TaskLogicParameters - - + + InterPresentationTime + + + TimeSpan.FromSeconds(it) + + + + + - - true + + PT0.5S - - SyncQuadColor + + TextureBank + + + + + + Item2 + + + TextureName + + + + + + + + + SN_1_1 + + + + + Texture0 + Image + + Texture2D + + + + UpdateSyncQuad + + + + Source1 + + + + + + + true + + + + SyncQuadColor + + + + + + + + + + + + + TaskLogicParameters + + + PresentationTime + + + TimeSpan.FromSeconds(it) + + + + + + + + + PT1S + + + + + 1 + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - TaskLogicParameters - - - PresentationTime - - - TimeSpan.FromSeconds(it) - - - - - - - - PT1S - + - - 1 - + - - + + + - + - - - - - - - - - - - - - - - - - - - - - - + + StartExperiment - - - - Algernon - 001 - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data + - SerialMessages + StopExperiment - - - - TODO - + - LogData + Logging - - Source1 + + Algernon + 001 + C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data - - - + + SerialMessages + + + + + + TODO + - FormatFileName - Generates a log filename based on the input timestamp. + LogData - - PathPrefix - Source1 - + + - - - Arduino - + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + Arduino + + + + + 1 + + + + + + + + + + + csv + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + - - - 1 - - - - + + + + - - - - - csv - - - - - 1 - - - - + - - {0}\{1}\{2}.{3} - Item1,Item2,Item3,Item4 + + C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T14-00-42\Arduino\Arduino.csv + false + false + None + true + it - - - - - - - - - - - - + + + + + + - - - - - - - - - - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T13-48-46\Arduino\Arduino.csv - false - false - None - true - it - - - - - - - + + @@ -771,38 +885,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + \ No newline at end of file diff --git a/src/ucl_open_hf_visual/rig.py b/src/ucl_open_hf_visual/rig.py index 072bb7c..bbb252e 100644 --- a/src/ucl_open_hf_visual/rig.py +++ b/src/ucl_open_hf_visual/rig.py @@ -8,11 +8,20 @@ from ucl_open_hf_visual import __semver__ +# TODO - should be part of main package? +# TODO - should be able to define generic number of sync quads (e.g. for different screens) +class SyncQuad(BaseSchema): + extent_x: float + extent_y: float + location_x: float + location_y: float + class MatrixArduino(SerialDeviceModule): device_type: Literal["MatrixArduino"] = "MatrixArduino" class UclOpenHfVisualRig(BaseSchema): version: Literal[__semver__] = __semver__ # type: ignore screen: Screen + sync_quad: SyncQuad arduino: MatrixArduino ... \ No newline at end of file From beb78ba4a00d554f12d8fc56bdc3c453865a6d67 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 14:53:05 +0000 Subject: [PATCH 06/17] Add visualizer --- .bonsai/Bonsai.config | 24 ++- .bonsai/Settings/src/main.editor | 3 +- .bonsai/Settings/src/main.layout | 103 ++++++++++++ src/main.bonsai | 271 ++++++++++++++++++++++++++++--- 4 files changed, 377 insertions(+), 24 deletions(-) diff --git a/.bonsai/Bonsai.config b/.bonsai/Bonsai.config index 88a88e9..27a19c4 100644 --- a/.bonsai/Bonsai.config +++ b/.bonsai/Bonsai.config @@ -5,8 +5,11 @@ - + + + + @@ -16,7 +19,8 @@ - + + @@ -29,6 +33,7 @@ + @@ -44,13 +49,17 @@ + + + + @@ -61,6 +70,7 @@ + @@ -73,8 +83,11 @@ - + + + + @@ -84,7 +97,8 @@ - + + @@ -96,6 +110,7 @@ + @@ -114,6 +129,7 @@ + diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 998d34c..227521a 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -13,8 +13,9 @@ - + + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index bab0b8c..4ef5947 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -72,4 +72,107 @@ + + + 120 + 130 + + + 418 + 377 + + Bonsai.Gui.TableLayoutPanelVisualizer + + + + Bonsai.Gui.GroupBoxVisualizer + + + + Bonsai.Gui.TableLayoutPanelVisualizer + + + + Bonsai.Gui.ButtonVisualizer + + + + + + Bonsai.Gui.ButtonVisualizer + + + + + + + + + + + + Bonsai.Gui.GroupBoxVisualizer + + + + Bonsai.Design.ObjectTextVisualizer + + + + + + + + + Bonsai.Gui.GroupBoxVisualizer + + + + Bonsai.Gui.ZedGraph.RollingGraphVisualizer + + + 0 + -1 + 1 + true + + + + + + + + Bonsai.Gui.GroupBoxVisualizer + + + + Bonsai.Gui.ZedGraph.RollingGraphVisualizer + + + 0 + 0 + 1.1 + true + + + + + + + + Bonsai.Gui.GroupBoxVisualizer + + + + Bonsai.Vision.Design.IplImageVisualizer + + + + + + + + + +
\ No newline at end of file diff --git a/src/main.bonsai b/src/main.bonsai index 5c0da02..500ffaa 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -5,11 +5,13 @@ xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core" xmlns:p1="clr-namespace:UclOpenHfVisualDataSchema;assembly=Extensions" xmlns:p2="clr-namespace:System.Reactive;assembly=System.Reactive.Core" + xmlns:p3="clr-namespace:;assembly=Extensions" xmlns:gl="clr-namespace:Bonsai.Shaders;assembly=Bonsai.Shaders" xmlns:res="clr-namespace:Bonsai.Resources;assembly=Bonsai.System" xmlns:scr="clr-namespace:Bonsai.Scripting.Expressions;assembly=Bonsai.Scripting.Expressions" xmlns:num="clr-namespace:Bonsai.Numerics;assembly=Bonsai.Numerics" - xmlns:p3="clr-namespace:;assembly=Extensions" + xmlns:gui="clr-namespace:Bonsai.Gui;assembly=Bonsai.Gui" + xmlns:zg="clr-namespace:Bonsai.Gui.ZedGraph;assembly=Bonsai.Gui.ZedGraph" xmlns:p4="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" xmlns="https://bonsai-rx.org/2018/workflow"> @@ -198,6 +200,15 @@ Arduino SerialMessages + + SerialMessages + + + + + + MatrixArduino + Display @@ -386,6 +397,20 @@ DrawCall + + + + + + + + + + + + + DebugView + DrawCall @@ -443,9 +468,9 @@ 1 0 0 - 0 - 0 - 0 + 1 + 1 + 1 1 @@ -469,16 +494,19 @@ + - + - - - - + + + - + + + + @@ -487,6 +515,8 @@ + + @@ -620,7 +650,7 @@ - SN_1_1 + SynthIm_1_1 @@ -750,6 +780,213 @@ + + Visualizer + + + + StartButton + true + true + Start + + + StartExperiment + + + + Source1 + + + + StartExperiment + + + + + + + + + + + + + StopButton + true + true + Stop + + + StopExperiment + + + + Source1 + + + + StopExperiment + + + + + + + + + + + + + ExperimentControls + true + true + 2 + 1 + + + + + + + ExperimentControls + true + true + Experiment Controls + + + + MatrixArduino + + + + Matrix Arduino + true + true + MatrixArduino + + + + MatrixArduino + + + Value.EncoderCount + + + None + 2 + + + + + + + + Encoder + true + true + Encoder + + + + MatrixArduino + + + Value.LastSyncPulseTime + + + + + + + 1 + + + + + + 2 + + + + None + 2 + + + + + + + + SyncPulse + true + true + Sync Pulse + + + + DebugView + + + + Displau + true + true + Display + + + + ExperimentPanel + true + true + 2 + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Logging @@ -760,10 +997,7 @@ C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data - SerialMessages - - - + MatrixArduino TODO @@ -854,7 +1088,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T14-00-42\Arduino\Arduino.csv + C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T14-52-00\Arduino\Arduino.csv false false None @@ -875,9 +1109,8 @@ - - - + + From c45d12555e27e2d813829ac834f81e40d6cd40b0 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 15:04:04 +0000 Subject: [PATCH 07/17] Add spinnaker settings --- examples/rig.py | 9 + src/DataSchemas/ucl_open_hf_visual.json | 61 +++++- src/Extensions/UclOpenHfVisual.Generated.cs | 202 +++++++++++++++++++- src/ucl_open_hf_visual/rig.py | 2 + 4 files changed, 272 insertions(+), 2 deletions(-) diff --git a/examples/rig.py b/examples/rig.py index 28dfe77..ae027af 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -10,6 +10,9 @@ from ucl_open.rigs.device import ( Screen, ) +from ucl_open.rigs.video import ( + SpinnakerCamera +) rig = UclOpenHfVisualRig( screen=Screen( @@ -25,6 +28,12 @@ port_name="COM10", baud_rate=1000000, new_line="\n" + ), + eye_camera=SpinnakerCamera( + exposure_time=5000, + serial_number="00000", + gain=0, + binning=1 ) ) diff --git a/src/DataSchemas/ucl_open_hf_visual.json b/src/DataSchemas/ucl_open_hf_visual.json index 36306d2..26e0046 100644 --- a/src/DataSchemas/ucl_open_hf_visual.json +++ b/src/DataSchemas/ucl_open_hf_visual.json @@ -348,6 +348,60 @@ "title": "Screen", "type": "object" }, + "SpinnakerCamera": { + "properties": { + "cameraType": { + "const": "Spinnaker", + "default": "Spinnaker", + "description": "Camera type discriminator for Spinnaker devices.", + "title": "CameraType", + "type": "string" + }, + "triggerFrequency": { + "default": 50, + "description": "The frequency at which the camera is triggered (in Hz).", + "minimum": 1, + "title": "TriggerFrequency", + "type": "number" + }, + "exposureTime": { + "default": 15000, + "description": "The exposure time for the camera (in microseconds).", + "minimum": 0, + "title": "ExposureTime", + "type": "number" + }, + "serialNumber": { + "default": "00000", + "description": "The serial number of the camera.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "SerialNumber" + }, + "gain": { + "default": 1, + "description": "The camera gain.", + "minimum": 0, + "title": "Gain", + "type": "number" + }, + "binning": { + "default": 1, + "description": "The binning setting for the camera.", + "minimum": 1, + "title": "Binning", + "type": "integer" + } + }, + "title": "SpinnakerCamera", + "type": "object" + }, "SyncQuad": { "properties": { "extentX": { @@ -393,12 +447,17 @@ "arduino": { "$ref": "#/$defs/MatrixArduino", "title": "Arduino" + }, + "eyeCamera": { + "$ref": "#/$defs/SpinnakerCamera", + "title": "EyeCamera" } }, "required": [ "screen", "syncQuad", - "arduino" + "arduino", + "eyeCamera" ], "title": "UclOpenHfVisualRig", "type": "object" diff --git a/src/Extensions/UclOpenHfVisual.Generated.cs b/src/Extensions/UclOpenHfVisual.Generated.cs index 6aec3f2..452e124 100644 --- a/src/Extensions/UclOpenHfVisual.Generated.cs +++ b/src/Extensions/UclOpenHfVisual.Generated.cs @@ -1210,6 +1210,181 @@ public override string ToString() } + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + [Bonsai.CombinatorAttribute(MethodName="Generate")] + public partial class SpinnakerCamera + { + + private string _cameraType; + + private double _triggerFrequency; + + private double _exposureTime; + + private string _serialNumber; + + private double _gain; + + private int _binning; + + public SpinnakerCamera() + { + _cameraType = "Spinnaker"; + _triggerFrequency = 50D; + _exposureTime = 15000D; + _gain = 1D; + _binning = 1; + } + + protected SpinnakerCamera(SpinnakerCamera other) + { + _cameraType = other._cameraType; + _triggerFrequency = other._triggerFrequency; + _exposureTime = other._exposureTime; + _serialNumber = other._serialNumber; + _gain = other._gain; + _binning = other._binning; + } + + /// + /// Camera type discriminator for Spinnaker devices. + /// + [Newtonsoft.Json.JsonPropertyAttribute("cameraType")] + [System.ComponentModel.DescriptionAttribute("Camera type discriminator for Spinnaker devices.")] + public string CameraType + { + get + { + return _cameraType; + } + set + { + _cameraType = value; + } + } + + /// + /// The frequency at which the camera is triggered (in Hz). + /// + [Newtonsoft.Json.JsonPropertyAttribute("triggerFrequency")] + [System.ComponentModel.DescriptionAttribute("The frequency at which the camera is triggered (in Hz).")] + public double TriggerFrequency + { + get + { + return _triggerFrequency; + } + set + { + _triggerFrequency = value; + } + } + + /// + /// The exposure time for the camera (in microseconds). + /// + [Newtonsoft.Json.JsonPropertyAttribute("exposureTime")] + [System.ComponentModel.DescriptionAttribute("The exposure time for the camera (in microseconds).")] + public double ExposureTime + { + get + { + return _exposureTime; + } + set + { + _exposureTime = value; + } + } + + /// + /// The serial number of the camera. + /// + [Newtonsoft.Json.JsonPropertyAttribute("serialNumber")] + [System.ComponentModel.DescriptionAttribute("The serial number of the camera.")] + public string SerialNumber + { + get + { + return _serialNumber; + } + set + { + _serialNumber = value; + } + } + + /// + /// The camera gain. + /// + [Newtonsoft.Json.JsonPropertyAttribute("gain")] + [System.ComponentModel.DescriptionAttribute("The camera gain.")] + public double Gain + { + get + { + return _gain; + } + set + { + _gain = value; + } + } + + /// + /// The binning setting for the camera. + /// + [Newtonsoft.Json.JsonPropertyAttribute("binning")] + [System.ComponentModel.DescriptionAttribute("The binning setting for the camera.")] + public int Binning + { + get + { + return _binning; + } + set + { + _binning = value; + } + } + + public System.IObservable Generate() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new SpinnakerCamera(this))); + } + + public System.IObservable Generate(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new SpinnakerCamera(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("CameraType = " + _cameraType + ", "); + stringBuilder.Append("TriggerFrequency = " + _triggerFrequency + ", "); + stringBuilder.Append("ExposureTime = " + _exposureTime + ", "); + stringBuilder.Append("SerialNumber = " + _serialNumber + ", "); + stringBuilder.Append("Gain = " + _gain + ", "); + stringBuilder.Append("Binning = " + _binning); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] @@ -1336,12 +1511,15 @@ public partial class UclOpenHfVisualRig private MatrixArduino _arduino; + private SpinnakerCamera _eyeCamera; + public UclOpenHfVisualRig() { _version = "0.0.0-rc1"; _screen = new Screen(); _syncQuad = new SyncQuad(); _arduino = new MatrixArduino(); + _eyeCamera = new SpinnakerCamera(); } protected UclOpenHfVisualRig(UclOpenHfVisualRig other) @@ -1350,6 +1528,7 @@ protected UclOpenHfVisualRig(UclOpenHfVisualRig other) _screen = other._screen; _syncQuad = other._syncQuad; _arduino = other._arduino; + _eyeCamera = other._eyeCamera; } [Newtonsoft.Json.JsonPropertyAttribute("version")] @@ -1407,6 +1586,20 @@ public MatrixArduino Arduino } } + [System.Xml.Serialization.XmlIgnoreAttribute()] + [Newtonsoft.Json.JsonPropertyAttribute("eyeCamera", Required=Newtonsoft.Json.Required.Always)] + public SpinnakerCamera EyeCamera + { + get + { + return _eyeCamera; + } + set + { + _eyeCamera = value; + } + } + public System.IObservable Generate() { return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new UclOpenHfVisualRig(this))); @@ -1422,7 +1615,8 @@ protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) stringBuilder.Append("Version = " + _version + ", "); stringBuilder.Append("Screen = " + _screen + ", "); stringBuilder.Append("SyncQuad = " + _syncQuad + ", "); - stringBuilder.Append("Arduino = " + _arduino); + stringBuilder.Append("Arduino = " + _arduino + ", "); + stringBuilder.Append("EyeCamera = " + _eyeCamera); return true; } @@ -1844,6 +2038,11 @@ public System.IObservable Process(System.IObservable source) return Process(source); } + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + public System.IObservable Process(System.IObservable source) { return Process(source); @@ -1885,6 +2084,7 @@ public System.IObservable Process(System.IObservable source) [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] diff --git a/src/ucl_open_hf_visual/rig.py b/src/ucl_open_hf_visual/rig.py index bbb252e..23ce0d2 100644 --- a/src/ucl_open_hf_visual/rig.py +++ b/src/ucl_open_hf_visual/rig.py @@ -5,6 +5,7 @@ from ucl_open.rigs.base import BaseSchema from ucl_open.rigs.device import Screen # TODO - change to ucl rigs from ucl_open.rigs.device import SerialDeviceModule +from ucl_open.rigs.video import SpinnakerCamera from ucl_open_hf_visual import __semver__ @@ -24,4 +25,5 @@ class UclOpenHfVisualRig(BaseSchema): screen: Screen sync_quad: SyncQuad arduino: MatrixArduino + eye_camera: SpinnakerCamera ... \ No newline at end of file From af194befd5432d5cfdef76537cfb176f2433d5d1 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 15:19:42 +0000 Subject: [PATCH 08/17] Add current texture monitor --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 18 ++ examples/session.py | 8 +- src/DataSchemas/ucl_open_hf_visual.json | 58 ++--- src/Extensions/UclOpenHfVisual.Generated.cs | 247 ++++++++++---------- src/main.bonsai | 163 ++++++++++--- src/ucl_open_hf_visual/regenerate.py | 3 +- src/ucl_open_hf_visual/session.py | 5 + 8 files changed, 325 insertions(+), 179 deletions(-) create mode 100644 src/ucl_open_hf_visual/session.py diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 227521a..6a5f4af 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index 4ef5947..f5b1eed 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -172,6 +172,24 @@ + + Bonsai.Gui.GroupBoxVisualizer + + + + Bonsai.Design.Visualizers.TimeSeriesVisualizer + + + 640 + 930 + 955 + true + + + + + + diff --git a/examples/session.py b/examples/session.py index 2874664..67f0d23 100644 --- a/examples/session.py +++ b/examples/session.py @@ -1,13 +1,15 @@ import datetime import os -from ucl_open.rigs.experiment import Experiment +from ucl_open_hf_visual.session import UclOpenSession # TODO - autofill experiment fields -session = Experiment( +# TODO - versions, repo configs etc. +session = UclOpenSession( workflow="main.bonsai", commit="", - repository_url="https://github.com/ucl-open/hf-visual" + repository_url="https://github.com/ucl-open/hf-visual", + logging_root_path="C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data" ) def main(path_seed: str = "./local/{schema}.json"): diff --git a/src/DataSchemas/ucl_open_hf_visual.json b/src/DataSchemas/ucl_open_hf_visual.json index 26e0046..04e1fbc 100644 --- a/src/DataSchemas/ucl_open_hf_visual.json +++ b/src/DataSchemas/ucl_open_hf_visual.json @@ -94,33 +94,6 @@ "title": "DisplayIntrinsics", "type": "object" }, - "Experiment": { - "description": "The base class for creating ucl-open experiment models.", - "properties": { - "workflow": { - "description": "Path to the workflow running the experiment.", - "title": "Workflow", - "type": "string" - }, - "commit": { - "description": "Commit hash of the experiment/rig repo.", - "title": "Commit", - "type": "string" - }, - "repositoryUrl": { - "description": "The URL of the git repository used to version experiment source code.", - "title": "RepositoryUrl", - "type": "string" - } - }, - "required": [ - "workflow", - "commit", - "repositoryUrl" - ], - "title": "Experiment", - "type": "object" - }, "MatrixArduino": { "properties": { "deviceType": { @@ -533,6 +506,37 @@ "title": "UclOpenHfVisualTaskParameters", "type": "object" }, + "UclOpenSession": { + "properties": { + "workflow": { + "description": "Path to the workflow running the experiment.", + "title": "Workflow", + "type": "string" + }, + "commit": { + "description": "Commit hash of the experiment/rig repo.", + "title": "Commit", + "type": "string" + }, + "repositoryUrl": { + "description": "The URL of the git repository used to version experiment source code.", + "title": "RepositoryUrl", + "type": "string" + }, + "loggingRootPath": { + "title": "LoggingRootPath", + "type": "string" + } + }, + "required": [ + "workflow", + "commit", + "repositoryUrl", + "loggingRootPath" + ], + "title": "UclOpenSession", + "type": "object" + }, "Vector3": { "properties": { "x": { diff --git a/src/Extensions/UclOpenHfVisual.Generated.cs b/src/Extensions/UclOpenHfVisual.Generated.cs index 452e124..4d7bcf8 100644 --- a/src/Extensions/UclOpenHfVisual.Generated.cs +++ b/src/Extensions/UclOpenHfVisual.Generated.cs @@ -321,117 +321,6 @@ public override string ToString() } - /// - /// The base class for creating ucl-open experiment models. - /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] - [System.ComponentModel.DescriptionAttribute("The base class for creating ucl-open experiment models.")] - [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] - [Bonsai.CombinatorAttribute(MethodName="Generate")] - public partial class Experiment - { - - private string _workflow; - - private string _commit; - - private string _repositoryUrl; - - public Experiment() - { - } - - protected Experiment(Experiment other) - { - _workflow = other._workflow; - _commit = other._commit; - _repositoryUrl = other._repositoryUrl; - } - - /// - /// Path to the workflow running the experiment. - /// - [Newtonsoft.Json.JsonPropertyAttribute("workflow", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("Path to the workflow running the experiment.")] - public string Workflow - { - get - { - return _workflow; - } - set - { - _workflow = value; - } - } - - /// - /// Commit hash of the experiment/rig repo. - /// - [Newtonsoft.Json.JsonPropertyAttribute("commit", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("Commit hash of the experiment/rig repo.")] - public string Commit - { - get - { - return _commit; - } - set - { - _commit = value; - } - } - - /// - /// The URL of the git repository used to version experiment source code. - /// - [Newtonsoft.Json.JsonPropertyAttribute("repositoryUrl", Required=Newtonsoft.Json.Required.Always)] - [System.ComponentModel.DescriptionAttribute("The URL of the git repository used to version experiment source code.")] - public string RepositoryUrl - { - get - { - return _repositoryUrl; - } - set - { - _repositoryUrl = value; - } - } - - public System.IObservable Generate() - { - return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new Experiment(this))); - } - - public System.IObservable Generate(System.IObservable source) - { - return System.Reactive.Linq.Observable.Select(source, _ => new Experiment(this)); - } - - protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) - { - stringBuilder.Append("Workflow = " + _workflow + ", "); - stringBuilder.Append("Commit = " + _commit + ", "); - stringBuilder.Append("RepositoryUrl = " + _repositoryUrl); - return true; - } - - public override string ToString() - { - System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); - stringBuilder.Append(GetType().Name); - stringBuilder.Append(" { "); - if (PrintMembers(stringBuilder)) - { - stringBuilder.Append(" "); - } - stringBuilder.Append("}"); - return stringBuilder.ToString(); - } - } - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] @@ -1878,6 +1767,130 @@ public override string ToString() } + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + [Bonsai.CombinatorAttribute(MethodName="Generate")] + public partial class UclOpenSession + { + + private string _workflow; + + private string _commit; + + private string _repositoryUrl; + + private string _loggingRootPath; + + public UclOpenSession() + { + } + + protected UclOpenSession(UclOpenSession other) + { + _workflow = other._workflow; + _commit = other._commit; + _repositoryUrl = other._repositoryUrl; + _loggingRootPath = other._loggingRootPath; + } + + /// + /// Path to the workflow running the experiment. + /// + [Newtonsoft.Json.JsonPropertyAttribute("workflow", Required=Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DescriptionAttribute("Path to the workflow running the experiment.")] + public string Workflow + { + get + { + return _workflow; + } + set + { + _workflow = value; + } + } + + /// + /// Commit hash of the experiment/rig repo. + /// + [Newtonsoft.Json.JsonPropertyAttribute("commit", Required=Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DescriptionAttribute("Commit hash of the experiment/rig repo.")] + public string Commit + { + get + { + return _commit; + } + set + { + _commit = value; + } + } + + /// + /// The URL of the git repository used to version experiment source code. + /// + [Newtonsoft.Json.JsonPropertyAttribute("repositoryUrl", Required=Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DescriptionAttribute("The URL of the git repository used to version experiment source code.")] + public string RepositoryUrl + { + get + { + return _repositoryUrl; + } + set + { + _repositoryUrl = value; + } + } + + [Newtonsoft.Json.JsonPropertyAttribute("loggingRootPath", Required=Newtonsoft.Json.Required.Always)] + public string LoggingRootPath + { + get + { + return _loggingRootPath; + } + set + { + _loggingRootPath = value; + } + } + + public System.IObservable Generate() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new UclOpenSession(this))); + } + + public System.IObservable Generate(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new UclOpenSession(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("Workflow = " + _workflow + ", "); + stringBuilder.Append("Commit = " + _commit + ", "); + stringBuilder.Append("RepositoryUrl = " + _repositoryUrl + ", "); + stringBuilder.Append("LoggingRootPath = " + _loggingRootPath); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] @@ -2018,11 +2031,6 @@ public System.IObservable Process(System.IObservable return Process(source); } - public System.IObservable Process(System.IObservable source) - { - return Process(source); - } - public System.IObservable Process(System.IObservable source) { return Process(source); @@ -2063,6 +2071,11 @@ public System.IObservable Process(System.IObservable(source); } + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + public System.IObservable Process(System.IObservable source) { return Process(source); @@ -2080,7 +2093,6 @@ public System.IObservable Process(System.IObservable source) [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] - [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] @@ -2089,6 +2101,7 @@ public System.IObservable Process(System.IObservable source) [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] public partial class DeserializeFromJson : Bonsai.Expressions.SingleArgumentExpressionBuilder { diff --git a/src/main.bonsai b/src/main.bonsai index 500ffaa..91ea96c 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -5,6 +5,7 @@ xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core" xmlns:p1="clr-namespace:UclOpenHfVisualDataSchema;assembly=Extensions" xmlns:p2="clr-namespace:System.Reactive;assembly=System.Reactive.Core" + xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:p3="clr-namespace:;assembly=Extensions" xmlns:gl="clr-namespace:Bonsai.Shaders;assembly=Bonsai.Shaders" xmlns:res="clr-namespace:Bonsai.Resources;assembly=Bonsai.System" @@ -34,7 +35,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\local\Experiment.json + C:\Users\neurogears\source\repos\ucl-open\hf-visual\local\UclOpenSession.json @@ -89,7 +90,7 @@ RawSessionSchema - + @@ -97,7 +98,7 @@ - SubjectSchema + SessionSchema RawRigSchema @@ -159,6 +160,9 @@ StopExperiment + + CurrentTexture + @@ -612,6 +616,40 @@ + + UpdateCurrentTexture + + + + Source1 + + + TaskLogicParameters + + + InterPresentationTexture + + + + + + Item2 + + + CurrentTexture + + + + + + + + + + + + + TaskLogicParameters @@ -689,6 +727,36 @@ + + UpdateCurrentTexture + + + + Source1 + + + TextureName + + + + + + Item2 + + + CurrentTexture + + + + + + + + + + + + TaskLogicParameters @@ -724,26 +792,28 @@ - - + + - - - - - - - - + + + + + + + + - + - + - + - + + + @@ -857,14 +927,14 @@ - MatrixArduino + CurrentTexture - Matrix Arduino + CurrentTexture true true - MatrixArduino + Current Texture @@ -930,12 +1000,26 @@ - Displau + Display true true Display + + MatrixArduino + + + Value.PhotodiodeVal + + + + Photodiode + true + true + Photodiode + + ExperimentPanel true @@ -958,17 +1042,17 @@ - + - + - + @@ -978,12 +1062,17 @@ - + - + + + + + + @@ -991,10 +1080,21 @@ Logging + + SessionSchema + + + LoggingRootPath + + + + + + Algernon 001 - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data MatrixArduino @@ -1088,7 +1188,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\temp_data\sub-Algernon\ses-001_date-2025-12-11T14-52-00\Arduino\Arduino.csv + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Algernon\ses-001_date-2025-12-11T15-19-14\Arduino\Arduino.csv false false None @@ -1109,8 +1209,11 @@ - - + + + + + diff --git a/src/ucl_open_hf_visual/regenerate.py b/src/ucl_open_hf_visual/regenerate.py index e52e27a..a5853a1 100644 --- a/src/ucl_open_hf_visual/regenerate.py +++ b/src/ucl_open_hf_visual/regenerate.py @@ -8,6 +8,7 @@ import ucl_open_hf_visual.rig import ucl_open_hf_visual.task import ucl_open_hf_visual.data_types +import ucl_open_hf_visual.session SCHEMA_ROOT = Path("./src/DataSchemas/") EXTENSIONS_ROOT = Path("./src/Extensions/") @@ -18,7 +19,7 @@ def main(): models = [ ucl_open_hf_visual.task.UclOpenHfVisualTaskLogic, ucl_open_hf_visual.rig.UclOpenHfVisualRig, - Experiment, + ucl_open_hf_visual.session.UclOpenSession, ucl_open_hf_visual.data_types.MatrixArduinoData ] model = pydantic.RootModel[Union[tuple(models)]] diff --git a/src/ucl_open_hf_visual/session.py b/src/ucl_open_hf_visual/session.py new file mode 100644 index 0000000..9a259cf --- /dev/null +++ b/src/ucl_open_hf_visual/session.py @@ -0,0 +1,5 @@ +from ucl_open.rigs.experiment import Experiment + +# TODO - should be part of ucl open rigs +class UclOpenSession(Experiment): + logging_root_path: str \ No newline at end of file From c358f7983b1a84daca4769b4bd7ea6749b6da3cb Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 15:26:58 +0000 Subject: [PATCH 09/17] Add other logging session variables --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 4 +- examples/session.py | 4 +- src/DataSchemas/ucl_open_hf_visual.json | 12 +- src/Extensions/UclOpenHfVisual.Generated.cs | 36 ++++- src/main.bonsai | 164 +++++++++++++++++++- src/ucl_open_hf_visual/session.py | 4 +- 7 files changed, 213 insertions(+), 13 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 6a5f4af..f4e3c7d 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index f5b1eed..3e769ea 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -181,8 +181,8 @@ 640 - 930 - 955 + 935 + 960 true diff --git a/examples/session.py b/examples/session.py index 67f0d23..b9b7aad 100644 --- a/examples/session.py +++ b/examples/session.py @@ -9,7 +9,9 @@ workflow="main.bonsai", commit="", repository_url="https://github.com/ucl-open/hf-visual", - logging_root_path="C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data" + logging_root_path="C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data", + animal_id="Plimbo", + session_id="001" ) def main(path_seed: str = "./local/{schema}.json"): diff --git a/src/DataSchemas/ucl_open_hf_visual.json b/src/DataSchemas/ucl_open_hf_visual.json index 04e1fbc..c50f3b1 100644 --- a/src/DataSchemas/ucl_open_hf_visual.json +++ b/src/DataSchemas/ucl_open_hf_visual.json @@ -526,13 +526,23 @@ "loggingRootPath": { "title": "LoggingRootPath", "type": "string" + }, + "animalId": { + "title": "AnimalId", + "type": "string" + }, + "sessionId": { + "title": "SessionId", + "type": "string" } }, "required": [ "workflow", "commit", "repositoryUrl", - "loggingRootPath" + "loggingRootPath", + "animalId", + "sessionId" ], "title": "UclOpenSession", "type": "object" diff --git a/src/Extensions/UclOpenHfVisual.Generated.cs b/src/Extensions/UclOpenHfVisual.Generated.cs index 4d7bcf8..8651dc5 100644 --- a/src/Extensions/UclOpenHfVisual.Generated.cs +++ b/src/Extensions/UclOpenHfVisual.Generated.cs @@ -1781,6 +1781,10 @@ public partial class UclOpenSession private string _loggingRootPath; + private string _animalId; + + private string _sessionId; + public UclOpenSession() { } @@ -1791,6 +1795,8 @@ protected UclOpenSession(UclOpenSession other) _commit = other._commit; _repositoryUrl = other._repositoryUrl; _loggingRootPath = other._loggingRootPath; + _animalId = other._animalId; + _sessionId = other._sessionId; } /// @@ -1857,6 +1863,32 @@ public string LoggingRootPath } } + [Newtonsoft.Json.JsonPropertyAttribute("animalId", Required=Newtonsoft.Json.Required.Always)] + public string AnimalId + { + get + { + return _animalId; + } + set + { + _animalId = value; + } + } + + [Newtonsoft.Json.JsonPropertyAttribute("sessionId", Required=Newtonsoft.Json.Required.Always)] + public string SessionId + { + get + { + return _sessionId; + } + set + { + _sessionId = value; + } + } + public System.IObservable Generate() { return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new UclOpenSession(this))); @@ -1872,7 +1904,9 @@ protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) stringBuilder.Append("Workflow = " + _workflow + ", "); stringBuilder.Append("Commit = " + _commit + ", "); stringBuilder.Append("RepositoryUrl = " + _repositoryUrl + ", "); - stringBuilder.Append("LoggingRootPath = " + _loggingRootPath); + stringBuilder.Append("LoggingRootPath = " + _loggingRootPath + ", "); + stringBuilder.Append("AnimalId = " + _animalId + ", "); + stringBuilder.Append("SessionId = " + _sessionId); return true; } diff --git a/src/main.bonsai b/src/main.bonsai index 91ea96c..62334bb 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -14,6 +14,7 @@ xmlns:gui="clr-namespace:Bonsai.Gui;assembly=Bonsai.Gui" xmlns:zg="clr-namespace:Bonsai.Gui.ZedGraph;assembly=Bonsai.Gui.ZedGraph" xmlns:p4="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" + xmlns:harp="clr-namespace:Bonsai.Harp;assembly=Bonsai.Harp" xmlns="https://bonsai-rx.org/2018/workflow"> @@ -188,8 +189,7 @@ COM10 1000000 - - + None 63 8 @@ -1091,6 +1091,28 @@ + + SessionSchema + + + AnimalId + + + + + + + + SessionSchema + + + SessionId + + + + + + Algernon 001 @@ -1188,7 +1210,126 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Algernon\ses-001_date-2025-12-11T15-19-14\Arduino\Arduino.csv + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Algernon\ses-001_date-2025-12-11T15-21-56\Arduino\Arduino.csv + false + false + None + true + it + + + + + + + + + + + + + + + CurrentTexture + + + MatrixArduino + + + Seconds + + + + + + + + + LogData + + + + Source1 + + + + + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + Texture + + + + + 1 + + + + + + + + + + + csv + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Algernon\ses-001_date-2025-12-11T15-21-56\Texture\Texture.csv false false None @@ -1211,9 +1352,20 @@ - - - + + + + + + + + + + + + + + diff --git a/src/ucl_open_hf_visual/session.py b/src/ucl_open_hf_visual/session.py index 9a259cf..e3d6ae1 100644 --- a/src/ucl_open_hf_visual/session.py +++ b/src/ucl_open_hf_visual/session.py @@ -2,4 +2,6 @@ # TODO - should be part of ucl open rigs class UclOpenSession(Experiment): - logging_root_path: str \ No newline at end of file + logging_root_path: str + animal_id: str + session_id: str \ No newline at end of file From 7cac29bd3067e8cea9e84da5c8be7d0472e02416 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 15:40:25 +0000 Subject: [PATCH 10/17] Add display intrinsics --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 4 +- examples/rig.py | 12 ++++- src/main.bonsai | 83 +++++++++++++++++++++++++------- 4 files changed, 80 insertions(+), 21 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index f4e3c7d..5250c8c 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index 3e769ea..aec044d 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -181,8 +181,8 @@ 640 - 935 - 960 + 876 + 882 true diff --git a/examples/rig.py b/examples/rig.py index ae027af..a075221 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -13,10 +13,20 @@ from ucl_open.rigs.video import ( SpinnakerCamera ) +from ucl_open.rigs.displays import ( + DisplayCalibration, + DisplayIntrinsics +) rig = UclOpenHfVisualRig( screen=Screen( - texture_assets_directory="../textures" + texture_assets_directory="../textures", + calibration={"main": DisplayCalibration( + intrinsics=DisplayIntrinsics( + frame_width=640, + frame_height=480 + ) + )} ), sync_quad=SyncQuad( extent_x=0.1, diff --git a/src/main.bonsai b/src/main.bonsai index 62334bb..d975035 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -189,7 +189,8 @@ COM10 1000000 - + + None 63 8 @@ -230,6 +231,44 @@ + + RigSchema + + + Screen.Calibration + + + + main + + + + Intrinsics.FrameWidth + + + + + + + + RigSchema + + + Screen.Calibration + + + + main + + + + Intrinsics.FrameHeight + + + + + + 640 @@ -489,28 +528,38 @@ - + + - - - + + + + - + - + - - - + + + - + - - + + + + + + + + + + @@ -688,7 +737,7 @@ - SynthIm_1_1 + SN_1_1 @@ -1114,7 +1163,7 @@ - Algernon + Plimbo 001 C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data @@ -1210,7 +1259,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Algernon\ses-001_date-2025-12-11T15-21-56\Arduino\Arduino.csv + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T15-34-28\Arduino\Arduino.csv false false None @@ -1329,7 +1378,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Algernon\ses-001_date-2025-12-11T15-21-56\Texture\Texture.csv + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T15-34-28\Texture\Texture.csv false false None From 25b073d10bfe974a9abf59bc56e92f7ce44a8db3 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 16:12:09 +0000 Subject: [PATCH 11/17] Add schema logging --- .bonsai/Bonsai.config | 4 +- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 27 +- .../UclOpen.Logging.0.1.3-dev0.nupkg | Bin 0 -> 8614 bytes src/main.bonsai | 358 +++++++++++++++++- 5 files changed, 379 insertions(+), 12 deletions(-) create mode 100644 local_packages/UclOpen.Logging.0.1.3-dev0.nupkg diff --git a/.bonsai/Bonsai.config b/.bonsai/Bonsai.config index 27a19c4..446a0e4 100644 --- a/.bonsai/Bonsai.config +++ b/.bonsai/Bonsai.config @@ -47,7 +47,7 @@ - + @@ -125,7 +125,7 @@ - + diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 5250c8c..227521a 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index aec044d..b93b276 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -150,8 +150,8 @@ 0 - 0 - 1.1 + 0.95 + 1.05 true @@ -181,8 +181,8 @@ 640 - 876 - 882 + 934 + 948 true @@ -193,4 +193,23 @@ + + + + false + + 144 + 156 + + + 994 + 196 + + Bonsai.Design.ObjectTextVisualizer + + + + + + \ No newline at end of file diff --git a/local_packages/UclOpen.Logging.0.1.3-dev0.nupkg b/local_packages/UclOpen.Logging.0.1.3-dev0.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..22c2e6845783655b412b67e509aa06492d0fafe1 GIT binary patch literal 8614 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 VDIp^LdBn@-6aBd&1e5>i{XcKLv#9_8 literal 0 HcmV?d00001 diff --git a/src/main.bonsai b/src/main.bonsai index d975035..cdfd568 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -15,6 +15,7 @@ xmlns:zg="clr-namespace:Bonsai.Gui.ZedGraph;assembly=Bonsai.Gui.ZedGraph" xmlns:p4="clr-namespace:UclOpen.Core;assembly=UclOpen.Core" xmlns:harp="clr-namespace:Bonsai.Harp;assembly=Bonsai.Harp" + xmlns:p5="clr-namespace:UclOpen.Logging;assembly=UclOpen.Logging" xmlns="https://bonsai-rx.org/2018/workflow"> @@ -511,9 +512,9 @@ 1 0 0 - 1 - 1 - 1 + 0 + 0 + 0 1 @@ -1259,7 +1260,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T15-34-28\Arduino\Arduino.csv + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\Arduino\Arduino.csv false false None @@ -1378,7 +1379,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T15-34-28\Texture\Texture.csv + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\Texture\Texture.csv false false None @@ -1397,6 +1398,343 @@ + + RigSchema + + + + None + + + + + PT1S + + + + LogDataSchema + + + + Source1 + + + + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + RigSchema + + + + + 1 + + + + + + + + + + + json + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\RigSchema\RigSchema.json + None + false + + + + + + + + + + + + + + + TaskLogicSchema + + + + None + + + + + PT1S + + + + LogDataSchema + + + + Source1 + + + + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + TaskLogicSchema + + + + + 1 + + + + + + + + + + + json + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\TaskLogicSchema\TaskLogicSchema.json + None + false + + + + + + + + + + + + + + + SessionSchema + + + + None + + + + + PT1S + + + + TODO + + + + LogDataSchema + + + + Source1 + + + + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + SessionSchema + + + + + 1 + + + + + + + + + + + json + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\SessionSchema\SessionSchema.json + None + false + + + + + + + + + + + + + @@ -1415,6 +1753,16 @@ + + + + + + + + + + From 904647a836fa343358a284514ccb3a2dfacd9182 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 16:31:44 +0000 Subject: [PATCH 12/17] Modify for extra arduino --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 2 +- examples/rig.py | 10 +- src/DataSchemas/ucl_open_hf_visual.json | 200 +++-- .../ParseMatrixPhotodiodeSerialDevice.cs | 22 + src/Extensions/UclOpenHfVisual.Generated.cs | 697 +++++++++++++----- src/main.bonsai | 189 ++++- src/ucl_open_hf_visual/data_types.py | 6 +- src/ucl_open_hf_visual/regenerate.py | 3 +- src/ucl_open_hf_visual/rig.py | 7 +- 10 files changed, 871 insertions(+), 267 deletions(-) create mode 100644 src/Extensions/ParseMatrixPhotodiodeSerialDevice.cs diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 227521a..f4e3c7d 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index b93b276..f476e24 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -195,7 +195,7 @@ - + false 144 diff --git a/examples/rig.py b/examples/rig.py index a075221..54afa08 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -5,6 +5,7 @@ ) from ucl_open_hf_visual.rig import ( MatrixArduino, + MatrixArduinoPhotodiode, SyncQuad ) from ucl_open.rigs.device import ( @@ -39,11 +40,10 @@ baud_rate=1000000, new_line="\n" ), - eye_camera=SpinnakerCamera( - exposure_time=5000, - serial_number="00000", - gain=0, - binning=1 + arduino_photodiode=MatrixArduinoPhotodiode( + port_name="COM11", + baud_rate=250000, + new_line="\n" ) ) diff --git a/src/DataSchemas/ucl_open_hf_visual.json b/src/DataSchemas/ucl_open_hf_visual.json index c50f3b1..5e1939c 100644 --- a/src/DataSchemas/ucl_open_hf_visual.json +++ b/src/DataSchemas/ucl_open_hf_visual.json @@ -252,6 +252,144 @@ "title": "MatrixArduinoData", "type": "object" }, + "MatrixArduinoPhotodiode": { + "properties": { + "deviceType": { + "const": "MatrixArduinoPhotodiode", + "default": "MatrixArduinoPhotodiode", + "title": "DeviceType", + "type": "string" + }, + "portName": { + "description": "The name of the device serial port.", + "examples": [ + "COMx" + ], + "title": "PortName", + "type": "string" + }, + "baudRate": { + "default": 9600, + "description": "Baud rate for serial communication.", + "title": "BaudRate", + "type": "integer" + }, + "encoding": { + "default": null, + "description": "Optional text encoding for interpreting incoming bytes.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Encoding" + }, + "newLine": { + "default": "\r\n", + "description": "Line termination sequence used to delimit incoming messages.", + "title": "NewLine", + "type": "string" + }, + "parity": { + "default": "None", + "description": "Parity checking mode for the serial port.", + "title": "Parity", + "type": "string" + }, + "parityReplace": { + "default": 63, + "description": "Byte used to replace invalid bytes detected by a parity error.", + "title": "ParityReplace", + "type": "integer" + }, + "dataBits": { + "default": 8, + "description": "Number of data bits per serial frame.", + "title": "DataBits", + "type": "integer" + }, + "stopBits": { + "default": "One", + "description": "Number of stop bits per serial frame.", + "title": "StopBits", + "type": "string" + }, + "handshake": { + "default": "None", + "description": "Hardware or software handshaking mode.", + "title": "Handshake", + "type": "string" + }, + "discardNull": { + "default": false, + "description": "Whether to discard null bytes appearing in the serial stream.", + "title": "DiscardNull", + "type": "boolean" + }, + "dtrEnable": { + "default": false, + "description": "Whether to enable Data Terminal Ready (DTR) control line.", + "title": "DtrEnable", + "type": "boolean" + }, + "rtsEnable": { + "default": false, + "description": "Whether to enable Request To Send (RTS) control line.", + "title": "RtsEnable", + "type": "boolean" + }, + "readBufferSize": { + "default": 4096, + "description": "Size, in bytes, of the read buffer.", + "title": "ReadBufferSize", + "type": "integer" + }, + "writeBufferSize": { + "default": 2048, + "description": "Size, in bytes, of the write buffer.", + "title": "WriteBufferSize", + "type": "integer" + }, + "receivedBytesThreshold": { + "default": 1, + "description": "Minimum number of bytes in the buffer that triggers a read event.", + "title": "ReceivedBytesThreshold", + "type": "integer" + }, + "serialMessageSubjectName": { + "default": "SerialMessages", + "description": "Name of the subject to which parsed serial messages are published.", + "title": "SerialMessageSubjectName", + "type": "string" + } + }, + "required": [ + "portName" + ], + "title": "MatrixArduinoPhotodiode", + "type": "object" + }, + "MatrixArduinoPhotodiodeData": { + "properties": { + "photodiodeVal": { + "title": "PhotodiodeVal", + "type": "integer" + }, + "syncVal": { + "title": "SyncVal", + "type": "integer" + } + }, + "required": [ + "photodiodeVal", + "syncVal" + ], + "title": "MatrixArduinoPhotodiodeData", + "type": "object" + }, "Screen": { "properties": { "deviceType": { @@ -321,60 +459,6 @@ "title": "Screen", "type": "object" }, - "SpinnakerCamera": { - "properties": { - "cameraType": { - "const": "Spinnaker", - "default": "Spinnaker", - "description": "Camera type discriminator for Spinnaker devices.", - "title": "CameraType", - "type": "string" - }, - "triggerFrequency": { - "default": 50, - "description": "The frequency at which the camera is triggered (in Hz).", - "minimum": 1, - "title": "TriggerFrequency", - "type": "number" - }, - "exposureTime": { - "default": 15000, - "description": "The exposure time for the camera (in microseconds).", - "minimum": 0, - "title": "ExposureTime", - "type": "number" - }, - "serialNumber": { - "default": "00000", - "description": "The serial number of the camera.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "SerialNumber" - }, - "gain": { - "default": 1, - "description": "The camera gain.", - "minimum": 0, - "title": "Gain", - "type": "number" - }, - "binning": { - "default": 1, - "description": "The binning setting for the camera.", - "minimum": 1, - "title": "Binning", - "type": "integer" - } - }, - "title": "SpinnakerCamera", - "type": "object" - }, "SyncQuad": { "properties": { "extentX": { @@ -421,16 +505,16 @@ "$ref": "#/$defs/MatrixArduino", "title": "Arduino" }, - "eyeCamera": { - "$ref": "#/$defs/SpinnakerCamera", - "title": "EyeCamera" + "arduinoPhotodiode": { + "$ref": "#/$defs/MatrixArduinoPhotodiode", + "title": "ArduinoPhotodiode" } }, "required": [ "screen", "syncQuad", "arduino", - "eyeCamera" + "arduinoPhotodiode" ], "title": "UclOpenHfVisualRig", "type": "object" diff --git a/src/Extensions/ParseMatrixPhotodiodeSerialDevice.cs b/src/Extensions/ParseMatrixPhotodiodeSerialDevice.cs new file mode 100644 index 0000000..49594bc --- /dev/null +++ b/src/Extensions/ParseMatrixPhotodiodeSerialDevice.cs @@ -0,0 +1,22 @@ +using System; +using System.Reactive.Linq; +using Bonsai; +using Bonsai.Harp; +using UclOpenHfVisualDataSchema; + +public class ParseMatrixPhotodiodeSerialDevice : Transform +{ + public override IObservable Process(IObservable source) + { + return source.Select(value => + { + var values = value.Split(','); + var matrixArduinoData = new MatrixArduinoPhotodiodeData + { + PhotodiodeVal = Convert.ToInt32(values[0]), + SyncVal = Convert.ToInt32(values[0]) + }; + return matrixArduinoData; + }); + } +} \ No newline at end of file diff --git a/src/Extensions/UclOpenHfVisual.Generated.cs b/src/Extensions/UclOpenHfVisual.Generated.cs index 8651dc5..1412428 100644 --- a/src/Extensions/UclOpenHfVisual.Generated.cs +++ b/src/Extensions/UclOpenHfVisual.Generated.cs @@ -879,6 +879,496 @@ public override string ToString() } + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + [Bonsai.CombinatorAttribute(MethodName="Generate")] + public partial class MatrixArduinoPhotodiode + { + + private string _deviceType; + + private string _portName; + + private int _baudRate; + + private string _encoding; + + private string _newLine; + + private string _parity; + + private int _parityReplace; + + private int _dataBits; + + private string _stopBits; + + private string _handshake; + + private bool _discardNull; + + private bool _dtrEnable; + + private bool _rtsEnable; + + private int _readBufferSize; + + private int _writeBufferSize; + + private int _receivedBytesThreshold; + + private string _serialMessageSubjectName; + + public MatrixArduinoPhotodiode() + { + _deviceType = "MatrixArduinoPhotodiode"; + _baudRate = 9600; + _newLine = "\r\n"; + _parity = "None"; + _parityReplace = 63; + _dataBits = 8; + _stopBits = "One"; + _handshake = "None"; + _discardNull = false; + _dtrEnable = false; + _rtsEnable = false; + _readBufferSize = 4096; + _writeBufferSize = 2048; + _receivedBytesThreshold = 1; + _serialMessageSubjectName = "SerialMessages"; + } + + protected MatrixArduinoPhotodiode(MatrixArduinoPhotodiode other) + { + _deviceType = other._deviceType; + _portName = other._portName; + _baudRate = other._baudRate; + _encoding = other._encoding; + _newLine = other._newLine; + _parity = other._parity; + _parityReplace = other._parityReplace; + _dataBits = other._dataBits; + _stopBits = other._stopBits; + _handshake = other._handshake; + _discardNull = other._discardNull; + _dtrEnable = other._dtrEnable; + _rtsEnable = other._rtsEnable; + _readBufferSize = other._readBufferSize; + _writeBufferSize = other._writeBufferSize; + _receivedBytesThreshold = other._receivedBytesThreshold; + _serialMessageSubjectName = other._serialMessageSubjectName; + } + + [Newtonsoft.Json.JsonPropertyAttribute("deviceType")] + public string DeviceType + { + get + { + return _deviceType; + } + set + { + _deviceType = value; + } + } + + /// + /// The name of the device serial port. + /// + [Newtonsoft.Json.JsonPropertyAttribute("portName", Required=Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DescriptionAttribute("The name of the device serial port.")] + public string PortName + { + get + { + return _portName; + } + set + { + _portName = value; + } + } + + /// + /// Baud rate for serial communication. + /// + [Newtonsoft.Json.JsonPropertyAttribute("baudRate")] + [System.ComponentModel.DescriptionAttribute("Baud rate for serial communication.")] + public int BaudRate + { + get + { + return _baudRate; + } + set + { + _baudRate = value; + } + } + + /// + /// Optional text encoding for interpreting incoming bytes. + /// + [Newtonsoft.Json.JsonPropertyAttribute("encoding")] + [System.ComponentModel.DescriptionAttribute("Optional text encoding for interpreting incoming bytes.")] + public string Encoding + { + get + { + return _encoding; + } + set + { + _encoding = value; + } + } + + /// + /// Line termination sequence used to delimit incoming messages. + /// + [Newtonsoft.Json.JsonPropertyAttribute("newLine")] + [System.ComponentModel.DescriptionAttribute("Line termination sequence used to delimit incoming messages.")] + public string NewLine + { + get + { + return _newLine; + } + set + { + _newLine = value; + } + } + + /// + /// Parity checking mode for the serial port. + /// + [Newtonsoft.Json.JsonPropertyAttribute("parity")] + [System.ComponentModel.DescriptionAttribute("Parity checking mode for the serial port.")] + public string Parity + { + get + { + return _parity; + } + set + { + _parity = value; + } + } + + /// + /// Byte used to replace invalid bytes detected by a parity error. + /// + [Newtonsoft.Json.JsonPropertyAttribute("parityReplace")] + [System.ComponentModel.DescriptionAttribute("Byte used to replace invalid bytes detected by a parity error.")] + public int ParityReplace + { + get + { + return _parityReplace; + } + set + { + _parityReplace = value; + } + } + + /// + /// Number of data bits per serial frame. + /// + [Newtonsoft.Json.JsonPropertyAttribute("dataBits")] + [System.ComponentModel.DescriptionAttribute("Number of data bits per serial frame.")] + public int DataBits + { + get + { + return _dataBits; + } + set + { + _dataBits = value; + } + } + + /// + /// Number of stop bits per serial frame. + /// + [Newtonsoft.Json.JsonPropertyAttribute("stopBits")] + [System.ComponentModel.DescriptionAttribute("Number of stop bits per serial frame.")] + public string StopBits + { + get + { + return _stopBits; + } + set + { + _stopBits = value; + } + } + + /// + /// Hardware or software handshaking mode. + /// + [Newtonsoft.Json.JsonPropertyAttribute("handshake")] + [System.ComponentModel.DescriptionAttribute("Hardware or software handshaking mode.")] + public string Handshake + { + get + { + return _handshake; + } + set + { + _handshake = value; + } + } + + /// + /// Whether to discard null bytes appearing in the serial stream. + /// + [Newtonsoft.Json.JsonPropertyAttribute("discardNull")] + [System.ComponentModel.DescriptionAttribute("Whether to discard null bytes appearing in the serial stream.")] + public bool DiscardNull + { + get + { + return _discardNull; + } + set + { + _discardNull = value; + } + } + + /// + /// Whether to enable Data Terminal Ready (DTR) control line. + /// + [Newtonsoft.Json.JsonPropertyAttribute("dtrEnable")] + [System.ComponentModel.DescriptionAttribute("Whether to enable Data Terminal Ready (DTR) control line.")] + public bool DtrEnable + { + get + { + return _dtrEnable; + } + set + { + _dtrEnable = value; + } + } + + /// + /// Whether to enable Request To Send (RTS) control line. + /// + [Newtonsoft.Json.JsonPropertyAttribute("rtsEnable")] + [System.ComponentModel.DescriptionAttribute("Whether to enable Request To Send (RTS) control line.")] + public bool RtsEnable + { + get + { + return _rtsEnable; + } + set + { + _rtsEnable = value; + } + } + + /// + /// Size, in bytes, of the read buffer. + /// + [Newtonsoft.Json.JsonPropertyAttribute("readBufferSize")] + [System.ComponentModel.DescriptionAttribute("Size, in bytes, of the read buffer.")] + public int ReadBufferSize + { + get + { + return _readBufferSize; + } + set + { + _readBufferSize = value; + } + } + + /// + /// Size, in bytes, of the write buffer. + /// + [Newtonsoft.Json.JsonPropertyAttribute("writeBufferSize")] + [System.ComponentModel.DescriptionAttribute("Size, in bytes, of the write buffer.")] + public int WriteBufferSize + { + get + { + return _writeBufferSize; + } + set + { + _writeBufferSize = value; + } + } + + /// + /// Minimum number of bytes in the buffer that triggers a read event. + /// + [Newtonsoft.Json.JsonPropertyAttribute("receivedBytesThreshold")] + [System.ComponentModel.DescriptionAttribute("Minimum number of bytes in the buffer that triggers a read event.")] + public int ReceivedBytesThreshold + { + get + { + return _receivedBytesThreshold; + } + set + { + _receivedBytesThreshold = value; + } + } + + /// + /// Name of the subject to which parsed serial messages are published. + /// + [Newtonsoft.Json.JsonPropertyAttribute("serialMessageSubjectName")] + [System.ComponentModel.DescriptionAttribute("Name of the subject to which parsed serial messages are published.")] + public string SerialMessageSubjectName + { + get + { + return _serialMessageSubjectName; + } + set + { + _serialMessageSubjectName = value; + } + } + + public System.IObservable Generate() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new MatrixArduinoPhotodiode(this))); + } + + public System.IObservable Generate(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new MatrixArduinoPhotodiode(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("DeviceType = " + _deviceType + ", "); + stringBuilder.Append("PortName = " + _portName + ", "); + stringBuilder.Append("BaudRate = " + _baudRate + ", "); + stringBuilder.Append("Encoding = " + _encoding + ", "); + stringBuilder.Append("NewLine = " + _newLine + ", "); + stringBuilder.Append("Parity = " + _parity + ", "); + stringBuilder.Append("ParityReplace = " + _parityReplace + ", "); + stringBuilder.Append("DataBits = " + _dataBits + ", "); + stringBuilder.Append("StopBits = " + _stopBits + ", "); + stringBuilder.Append("Handshake = " + _handshake + ", "); + stringBuilder.Append("DiscardNull = " + _discardNull + ", "); + stringBuilder.Append("DtrEnable = " + _dtrEnable + ", "); + stringBuilder.Append("RtsEnable = " + _rtsEnable + ", "); + stringBuilder.Append("ReadBufferSize = " + _readBufferSize + ", "); + stringBuilder.Append("WriteBufferSize = " + _writeBufferSize + ", "); + stringBuilder.Append("ReceivedBytesThreshold = " + _receivedBytesThreshold + ", "); + stringBuilder.Append("SerialMessageSubjectName = " + _serialMessageSubjectName); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + [Bonsai.CombinatorAttribute(MethodName="Generate")] + public partial class MatrixArduinoPhotodiodeData + { + + private int _photodiodeVal; + + private int _syncVal; + + public MatrixArduinoPhotodiodeData() + { + } + + protected MatrixArduinoPhotodiodeData(MatrixArduinoPhotodiodeData other) + { + _photodiodeVal = other._photodiodeVal; + _syncVal = other._syncVal; + } + + [Newtonsoft.Json.JsonPropertyAttribute("photodiodeVal", Required=Newtonsoft.Json.Required.Always)] + public int PhotodiodeVal + { + get + { + return _photodiodeVal; + } + set + { + _photodiodeVal = value; + } + } + + [Newtonsoft.Json.JsonPropertyAttribute("syncVal", Required=Newtonsoft.Json.Required.Always)] + public int SyncVal + { + get + { + return _syncVal; + } + set + { + _syncVal = value; + } + } + + public System.IObservable Generate() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new MatrixArduinoPhotodiodeData(this))); + } + + public System.IObservable Generate(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new MatrixArduinoPhotodiodeData(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("PhotodiodeVal = " + _photodiodeVal + ", "); + stringBuilder.Append("SyncVal = " + _syncVal); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] @@ -1099,181 +1589,6 @@ public override string ToString() } - [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] - [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] - [Bonsai.CombinatorAttribute(MethodName="Generate")] - public partial class SpinnakerCamera - { - - private string _cameraType; - - private double _triggerFrequency; - - private double _exposureTime; - - private string _serialNumber; - - private double _gain; - - private int _binning; - - public SpinnakerCamera() - { - _cameraType = "Spinnaker"; - _triggerFrequency = 50D; - _exposureTime = 15000D; - _gain = 1D; - _binning = 1; - } - - protected SpinnakerCamera(SpinnakerCamera other) - { - _cameraType = other._cameraType; - _triggerFrequency = other._triggerFrequency; - _exposureTime = other._exposureTime; - _serialNumber = other._serialNumber; - _gain = other._gain; - _binning = other._binning; - } - - /// - /// Camera type discriminator for Spinnaker devices. - /// - [Newtonsoft.Json.JsonPropertyAttribute("cameraType")] - [System.ComponentModel.DescriptionAttribute("Camera type discriminator for Spinnaker devices.")] - public string CameraType - { - get - { - return _cameraType; - } - set - { - _cameraType = value; - } - } - - /// - /// The frequency at which the camera is triggered (in Hz). - /// - [Newtonsoft.Json.JsonPropertyAttribute("triggerFrequency")] - [System.ComponentModel.DescriptionAttribute("The frequency at which the camera is triggered (in Hz).")] - public double TriggerFrequency - { - get - { - return _triggerFrequency; - } - set - { - _triggerFrequency = value; - } - } - - /// - /// The exposure time for the camera (in microseconds). - /// - [Newtonsoft.Json.JsonPropertyAttribute("exposureTime")] - [System.ComponentModel.DescriptionAttribute("The exposure time for the camera (in microseconds).")] - public double ExposureTime - { - get - { - return _exposureTime; - } - set - { - _exposureTime = value; - } - } - - /// - /// The serial number of the camera. - /// - [Newtonsoft.Json.JsonPropertyAttribute("serialNumber")] - [System.ComponentModel.DescriptionAttribute("The serial number of the camera.")] - public string SerialNumber - { - get - { - return _serialNumber; - } - set - { - _serialNumber = value; - } - } - - /// - /// The camera gain. - /// - [Newtonsoft.Json.JsonPropertyAttribute("gain")] - [System.ComponentModel.DescriptionAttribute("The camera gain.")] - public double Gain - { - get - { - return _gain; - } - set - { - _gain = value; - } - } - - /// - /// The binning setting for the camera. - /// - [Newtonsoft.Json.JsonPropertyAttribute("binning")] - [System.ComponentModel.DescriptionAttribute("The binning setting for the camera.")] - public int Binning - { - get - { - return _binning; - } - set - { - _binning = value; - } - } - - public System.IObservable Generate() - { - return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new SpinnakerCamera(this))); - } - - public System.IObservable Generate(System.IObservable source) - { - return System.Reactive.Linq.Observable.Select(source, _ => new SpinnakerCamera(this)); - } - - protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) - { - stringBuilder.Append("CameraType = " + _cameraType + ", "); - stringBuilder.Append("TriggerFrequency = " + _triggerFrequency + ", "); - stringBuilder.Append("ExposureTime = " + _exposureTime + ", "); - stringBuilder.Append("SerialNumber = " + _serialNumber + ", "); - stringBuilder.Append("Gain = " + _gain + ", "); - stringBuilder.Append("Binning = " + _binning); - return true; - } - - public override string ToString() - { - System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); - stringBuilder.Append(GetType().Name); - stringBuilder.Append(" { "); - if (PrintMembers(stringBuilder)) - { - stringBuilder.Append(" "); - } - stringBuilder.Append("}"); - return stringBuilder.ToString(); - } - } - - [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.6.1.0 (Newtonsoft.Json v13.0.0.0)")] [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] [Bonsai.CombinatorAttribute(MethodName="Generate")] @@ -1400,7 +1715,7 @@ public partial class UclOpenHfVisualRig private MatrixArduino _arduino; - private SpinnakerCamera _eyeCamera; + private MatrixArduinoPhotodiode _arduinoPhotodiode; public UclOpenHfVisualRig() { @@ -1408,7 +1723,7 @@ public UclOpenHfVisualRig() _screen = new Screen(); _syncQuad = new SyncQuad(); _arduino = new MatrixArduino(); - _eyeCamera = new SpinnakerCamera(); + _arduinoPhotodiode = new MatrixArduinoPhotodiode(); } protected UclOpenHfVisualRig(UclOpenHfVisualRig other) @@ -1417,7 +1732,7 @@ protected UclOpenHfVisualRig(UclOpenHfVisualRig other) _screen = other._screen; _syncQuad = other._syncQuad; _arduino = other._arduino; - _eyeCamera = other._eyeCamera; + _arduinoPhotodiode = other._arduinoPhotodiode; } [Newtonsoft.Json.JsonPropertyAttribute("version")] @@ -1476,16 +1791,16 @@ public MatrixArduino Arduino } [System.Xml.Serialization.XmlIgnoreAttribute()] - [Newtonsoft.Json.JsonPropertyAttribute("eyeCamera", Required=Newtonsoft.Json.Required.Always)] - public SpinnakerCamera EyeCamera + [Newtonsoft.Json.JsonPropertyAttribute("arduinoPhotodiode", Required=Newtonsoft.Json.Required.Always)] + public MatrixArduinoPhotodiode ArduinoPhotodiode { get { - return _eyeCamera; + return _arduinoPhotodiode; } set { - _eyeCamera = value; + _arduinoPhotodiode = value; } } @@ -1505,7 +1820,7 @@ protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) stringBuilder.Append("Screen = " + _screen + ", "); stringBuilder.Append("SyncQuad = " + _syncQuad + ", "); stringBuilder.Append("Arduino = " + _arduino + ", "); - stringBuilder.Append("EyeCamera = " + _eyeCamera); + stringBuilder.Append("ArduinoPhotodiode = " + _arduinoPhotodiode); return true; } @@ -2075,14 +2390,19 @@ public System.IObservable Process(System.IObservable return Process(source); } - public System.IObservable Process(System.IObservable source) + public System.IObservable Process(System.IObservable source) { - return Process(source); + return Process(source); + } + + public System.IObservable Process(System.IObservable source) + { + return Process(source); } - public System.IObservable Process(System.IObservable source) + public System.IObservable Process(System.IObservable source) { - return Process(source); + return Process(source); } public System.IObservable Process(System.IObservable source) @@ -2129,8 +2449,9 @@ public System.IObservable Process(System.IObservable source) [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] - [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] diff --git a/src/main.bonsai b/src/main.bonsai index cdfd568..8af293c 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -190,8 +190,7 @@ COM10 1000000 - - + None 63 8 @@ -204,10 +203,51 @@ 2048 1 Arduino - SerialMessages + SerialMessagesMatrix - SerialMessages + RigSchema + + + ArduinoPhotodiode + + + + + + + + + + COM10 + 1000000 + + + None + 63 + 8 + One + None + false + false + false + 4096 + 2048 + 1 + Arduino + SerialMessagesPhotodiode + + + SerialMessagesPhotodiode + + + + + + MatrixPhotodiode + + + SerialMessagesMatrix @@ -571,6 +611,11 @@ + + + + + @@ -1279,6 +1324,125 @@ + + MatrixPhotodiode + + + MatrixArduino + + + Seconds + + + + + + + + + LogData + + + + Source1 + + + + + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + ArduinoPhotodiode + + + + + 1 + + + + + + + + + + + csv + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\Arduino\Arduino.csv + false + false + None + true + it + + + + + + + + + + + + + CurrentTexture @@ -1753,16 +1917,21 @@ - + - + + - + - - - + + + + + + + diff --git a/src/ucl_open_hf_visual/data_types.py b/src/ucl_open_hf_visual/data_types.py index 06933f7..0e0e3f0 100644 --- a/src/ucl_open_hf_visual/data_types.py +++ b/src/ucl_open_hf_visual/data_types.py @@ -6,4 +6,8 @@ class MatrixArduinoData(BaseSchema): lick_count_right: int last_sync_pulse_time: int photodiode_val: int - current_ms: int \ No newline at end of file + current_ms: int + +class MatrixArduinoPhotodiodeData(BaseSchema): + photodiode_val: int + sync_val: int \ No newline at end of file diff --git a/src/ucl_open_hf_visual/regenerate.py b/src/ucl_open_hf_visual/regenerate.py index a5853a1..ada4d08 100644 --- a/src/ucl_open_hf_visual/regenerate.py +++ b/src/ucl_open_hf_visual/regenerate.py @@ -20,7 +20,8 @@ def main(): ucl_open_hf_visual.task.UclOpenHfVisualTaskLogic, ucl_open_hf_visual.rig.UclOpenHfVisualRig, ucl_open_hf_visual.session.UclOpenSession, - ucl_open_hf_visual.data_types.MatrixArduinoData + ucl_open_hf_visual.data_types.MatrixArduinoData, + ucl_open_hf_visual.data_types.MatrixArduinoPhotodiodeData ] model = pydantic.RootModel[Union[tuple(models)]] diff --git a/src/ucl_open_hf_visual/rig.py b/src/ucl_open_hf_visual/rig.py index 23ce0d2..4e0270e 100644 --- a/src/ucl_open_hf_visual/rig.py +++ b/src/ucl_open_hf_visual/rig.py @@ -5,7 +5,7 @@ from ucl_open.rigs.base import BaseSchema from ucl_open.rigs.device import Screen # TODO - change to ucl rigs from ucl_open.rigs.device import SerialDeviceModule -from ucl_open.rigs.video import SpinnakerCamera +# from ucl_open.rigs.video import ArducamOV9180 from ucl_open_hf_visual import __semver__ @@ -19,11 +19,14 @@ class SyncQuad(BaseSchema): class MatrixArduino(SerialDeviceModule): device_type: Literal["MatrixArduino"] = "MatrixArduino" + +class MatrixArduinoPhotodiode(SerialDeviceModule): + device_type: Literal["MatrixArduinoPhotodiode"] = "MatrixArduinoPhotodiode" class UclOpenHfVisualRig(BaseSchema): version: Literal[__semver__] = __semver__ # type: ignore screen: Screen sync_quad: SyncQuad arduino: MatrixArduino - eye_camera: SpinnakerCamera + arduino_photodiode: MatrixArduinoPhotodiode ... \ No newline at end of file From 0ca4dd9e4c1237c226f48e94b5ae1f36005ba9d2 Mon Sep 17 00:00:00 2001 From: RoboDoig Date: Thu, 11 Dec 2025 16:50:07 +0000 Subject: [PATCH 13/17] quick testing --- .bonsai/Settings/src/main.editor | 2 +- examples/rig.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index f4e3c7d..227521a 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/examples/rig.py b/examples/rig.py index 54afa08..933efb4 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -41,7 +41,7 @@ new_line="\n" ), arduino_photodiode=MatrixArduinoPhotodiode( - port_name="COM11", + port_name="COM10", baud_rate=250000, new_line="\n" ) From 9b39f7ff6ebc38a1dac0795494ff2177ae090b8a Mon Sep 17 00:00:00 2001 From: eabhorrocks <36156626+eabhorrocks@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:14:28 +0000 Subject: [PATCH 14/17] Setting up for test run --- .bonsai/Settings/src/main.editor | 2 +- examples/rig.py | 4 ++-- examples/session.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 227521a..4d66df5 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/examples/rig.py b/examples/rig.py index 933efb4..b623194 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -36,12 +36,12 @@ location_y=1 ), arduino=MatrixArduino( - port_name="COM10", + port_name="COM4", baud_rate=1000000, new_line="\n" ), arduino_photodiode=MatrixArduinoPhotodiode( - port_name="COM10", + port_name="COM5", baud_rate=250000, new_line="\n" ) diff --git a/examples/session.py b/examples/session.py index b9b7aad..9cdff97 100644 --- a/examples/session.py +++ b/examples/session.py @@ -9,7 +9,7 @@ workflow="main.bonsai", commit="", repository_url="https://github.com/ucl-open/hf-visual", - logging_root_path="C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data", + logging_root_path="C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data", animal_id="Plimbo", session_id="001" ) From 207f3a2a9171b2c245f30cf09f1e9285fda2c4f9 Mon Sep 17 00:00:00 2001 From: eabhorrocks <36156626+eabhorrocks@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:38:23 +0000 Subject: [PATCH 15/17] Getting things to work on rig --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 57 +++++++++++++++++++++++--- examples/rig.py | 8 ++-- src/main.bonsai | 70 ++++++++++++++++---------------- 4 files changed, 91 insertions(+), 46 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 4d66df5..227521a 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index f476e24..679d45b 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -2,6 +2,21 @@ + + false + + 130 + 130 + + + 336 + 82 + + Bonsai.Design.ObjectTextVisualizer + + + + @@ -70,12 +85,42 @@ + + false + + 468 + 132 + + + 336 + 75 + + Bonsai.Design.ObjectTextVisualizer + + + + + + false + + 182 + 182 + + + 336 + 137 + + Bonsai.Design.ObjectTextVisualizer + + + + - 120 - 130 + 119 + 205 418 @@ -150,8 +195,8 @@ 0 - 0.95 - 1.05 + 0 + 1.1 true @@ -181,8 +226,8 @@ 640 - 934 - 948 + 0 + 160 true diff --git a/examples/rig.py b/examples/rig.py index b623194..53ffcfc 100644 --- a/examples/rig.py +++ b/examples/rig.py @@ -32,18 +32,18 @@ sync_quad=SyncQuad( extent_x=0.1, extent_y=0.1, - location_x=1, - location_y=1 + location_x=-1, + location_y=-1 ), arduino=MatrixArduino( port_name="COM4", baud_rate=1000000, - new_line="\n" + new_line=r"\n" ), arduino_photodiode=MatrixArduinoPhotodiode( port_name="COM5", baud_rate=250000, - new_line="\n" + new_line=r"\n" ) ) diff --git a/src/main.bonsai b/src/main.bonsai index 8af293c..91e7048 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -37,7 +37,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\local\UclOpenSession.json + C:\Users\saleem_lab\Documents\GitHub\hf-visual\local\UclOpenSession.json @@ -48,7 +48,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\local\UclOpenHfVisualRig.json + C:\Users\saleem_lab\Documents\GitHub\hf-visual\local\UclOpenHfVisualRig.json @@ -59,7 +59,7 @@ - C:\Users\neurogears\source\repos\ucl-open\hf-visual\local\UclOpenHfVisualTaskLogic.json + C:\Users\saleem_lab\Documents\GitHub\hf-visual\local\UclOpenHfVisualTaskLogic.json @@ -187,17 +187,17 @@ - COM10 + COM4 1000000 - - + us-ascii + \n None 63 8 One None false - false + true false 4096 2048 @@ -219,41 +219,41 @@ - COM10 - 1000000 - - + COM5 + 250000 + us-ascii + \n None 63 8 One None false - false + true false 4096 2048 1 - Arduino + ArduinoPhotodiode SerialMessagesPhotodiode - SerialMessagesPhotodiode + SerialMessagesMatrix - + - MatrixPhotodiode + MatrixArduino - SerialMessagesMatrix + SerialMessagesPhotodiode - + - MatrixArduino + MatrixPhotodiode Display @@ -321,7 +321,7 @@ true Resizable - Normal + Fullscreen Second 60 120 @@ -548,13 +548,13 @@ 0.1 0.1 - 1 - 1 + -1 + -1 0 0 - 0 - 0 - 0 + 1 + 1 + 1 1 @@ -783,7 +783,7 @@ - SN_1_1 + SynthIm_1_1 @@ -1102,10 +1102,10 @@ - MatrixArduino + MatrixPhotodiode - Value.PhotodiodeVal + PhotodiodeVal @@ -1211,7 +1211,7 @@ Plimbo 001 - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data MatrixArduino @@ -1305,7 +1305,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\Arduino\Arduino.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\Arduino\Arduino.csv false false None @@ -1424,7 +1424,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\Arduino\Arduino.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-27-51\ArduinoPhotodiode\ArduinoPhotodiode.csv false false None @@ -1543,7 +1543,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\Texture\Texture.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\Texture\Texture.csv false false None @@ -1657,7 +1657,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\RigSchema\RigSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\RigSchema\RigSchema.json None false @@ -1768,7 +1768,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\TaskLogicSchema\TaskLogicSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\TaskLogicSchema\TaskLogicSchema.json None false @@ -1883,7 +1883,7 @@ - C:/Users/neurogears/source/repos/ucl-open/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T16-11-23\SessionSchema\SessionSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\SessionSchema\SessionSchema.json None false From c87bd976f4ff51eb3bdfd2c8de465c4b7fc15d70 Mon Sep 17 00:00:00 2001 From: eabhorrocks <36156626+eabhorrocks@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:49:07 +0000 Subject: [PATCH 16/17] Add basic camera logging --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 22 ++- src/main.bonsai | 277 ++++++++++++++++++++++++++++--- 3 files changed, 272 insertions(+), 29 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index 227521a..f4e3c7d 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index 679d45b..c61a78f 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -119,12 +119,12 @@ - 119 - 205 + 451 + 134 418 - 377 + 606 Bonsai.Gui.TableLayoutPanelVisualizer @@ -177,8 +177,8 @@ 0 - -1 - 1 + -10000 + 100000 true @@ -226,8 +226,8 @@ 640 - 0 - 160 + 120 + 150 true @@ -235,12 +235,18 @@ + + Bonsai.Vision.Design.IplImageVisualizer + + + + - + false 144 diff --git a/src/main.bonsai b/src/main.bonsai index 91e7048..5d7b113 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -10,6 +10,7 @@ xmlns:gl="clr-namespace:Bonsai.Shaders;assembly=Bonsai.Shaders" xmlns:res="clr-namespace:Bonsai.Resources;assembly=Bonsai.System" xmlns:scr="clr-namespace:Bonsai.Scripting.Expressions;assembly=Bonsai.Scripting.Expressions" + xmlns:cv="clr-namespace:Bonsai.Vision;assembly=Bonsai.Vision" xmlns:num="clr-namespace:Bonsai.Numerics;assembly=Bonsai.Numerics" xmlns:gui="clr-namespace:Bonsai.Gui;assembly=Bonsai.Gui" xmlns:zg="clr-namespace:Bonsai.Gui.ZedGraph;assembly=Bonsai.Gui.ZedGraph" @@ -604,6 +605,21 @@ + + + 0 + + + + + MatrixArduino + + + + + + CameraStream + @@ -616,6 +632,9 @@ + + + @@ -1115,15 +1134,27 @@ Photodiode + + CameraStream + + ExperimentPanel true true 2 - 3 + 4 - + + + + + + + + + @@ -1137,17 +1168,17 @@ - + - + - + @@ -1157,17 +1188,19 @@ - + - + - + + + @@ -1305,7 +1338,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\Arduino\Arduino.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\Arduino\Arduino.csv false false None @@ -1424,7 +1457,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-27-51\ArduinoPhotodiode\ArduinoPhotodiode.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\ArduinoPhotodiode\ArduinoPhotodiode.csv false false None @@ -1543,7 +1576,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\Texture\Texture.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\Texture\Texture.csv false false None @@ -1562,6 +1595,205 @@ + + CameraStream + + + MatrixArduino + + + Seconds + + + + + + + + + LogVideo + + + + Source1 + + + + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + VideoData + + + + + + + + + + + csv + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + None + true + Seconds,Value.Size + + + Value + + + FormatFileName + Generates a log filename based on the input timestamp. + + + + PathPrefix + + + Source1 + + + + + + + VideoData + + + + + + + + + + + avi + + + + + 1 + + + + + + + {0}\{1}\{2}.{3} + Item1,Item2,Item3,Item4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + None + true + false + FMP4 + 50 + + 0 + 0 + + NearestNeighbor + + + + + + + + + + + + + + + + + + + RigSchema @@ -1657,7 +1889,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\RigSchema\RigSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\RigSchema\RigSchema.json None false @@ -1768,7 +2000,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\TaskLogicSchema\TaskLogicSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\TaskLogicSchema\TaskLogicSchema.json None false @@ -1883,7 +2115,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-34-28\SessionSchema\SessionSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\SessionSchema\SessionSchema.json None false @@ -1922,16 +2154,21 @@ - + - + + - + - - - + + + + + + + From f797f4603c528419efa62695e09bc855ec3ab03b Mon Sep 17 00:00:00 2001 From: eabhorrocks <36156626+eabhorrocks@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:56:30 +0000 Subject: [PATCH 17/17] specific settings for test run --- .bonsai/Settings/src/main.editor | 2 +- .bonsai/Settings/src/main.layout | 8 ++++---- examples/session.py | 2 +- examples/task.py | 6 +++--- src/main.bonsai | 24 ++++++++++++------------ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.bonsai/Settings/src/main.editor b/.bonsai/Settings/src/main.editor index f4e3c7d..227521a 100644 --- a/.bonsai/Settings/src/main.editor +++ b/.bonsai/Settings/src/main.editor @@ -4,7 +4,7 @@ - + diff --git a/.bonsai/Settings/src/main.layout b/.bonsai/Settings/src/main.layout index c61a78f..16a2d20 100644 --- a/.bonsai/Settings/src/main.layout +++ b/.bonsai/Settings/src/main.layout @@ -177,8 +177,8 @@ 0 - -10000 - 100000 + -1 + 1 true @@ -226,8 +226,8 @@ 640 - 120 - 150 + 10 + 30 true diff --git a/examples/session.py b/examples/session.py index 9cdff97..100f045 100644 --- a/examples/session.py +++ b/examples/session.py @@ -10,7 +10,7 @@ commit="", repository_url="https://github.com/ucl-open/hf-visual", logging_root_path="C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data", - animal_id="Plimbo", + animal_id="mouse-001", session_id="001" ) diff --git a/examples/task.py b/examples/task.py index cb12854..8617adf 100644 --- a/examples/task.py +++ b/examples/task.py @@ -7,10 +7,10 @@ task_logic = UclOpenHfVisualTaskLogic( task_parameters=UclOpenHfVisualTaskParameters( - available_textures=["SN_1_1", "SynthIm_1_1"], + available_textures=["SN_1_1", "SynthIm_1_1", "SynthIm_1_5"], inter_presentation_texture="blankStim_1", - presentation_time=1, - inter_presentation_time=0.5 + presentation_time=0.25, + inter_presentation_time=0.75 ), ) diff --git a/src/main.bonsai b/src/main.bonsai index 5d7b113..70224c3 100644 --- a/src/main.bonsai +++ b/src/main.bonsai @@ -553,9 +553,9 @@ -1 0 0 - 1 - 1 - 1 + 0 + 0 + 0 1 @@ -802,7 +802,7 @@ - SynthIm_1_1 + SN_1_1 @@ -1338,7 +1338,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\Arduino\Arduino.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\Arduino\Arduino.csv false false None @@ -1457,7 +1457,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\ArduinoPhotodiode\ArduinoPhotodiode.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\ArduinoPhotodiode\ArduinoPhotodiode.csv false false None @@ -1576,7 +1576,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\Texture\Texture.csv + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\Texture\Texture.csv false false None @@ -1685,7 +1685,7 @@ - + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\VideoData\VideoData.csv false false None @@ -1764,7 +1764,7 @@ - + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\VideoData\VideoData.avi None true false @@ -1889,7 +1889,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\RigSchema\RigSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\RigSchema\RigSchema.json None false @@ -2000,7 +2000,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\TaskLogicSchema\TaskLogicSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\TaskLogicSchema\TaskLogicSchema.json None false @@ -2115,7 +2115,7 @@ - C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-40-28\SessionSchema\SessionSchema.json + C:/Users/saleem_lab/Documents/GitHub/hf-visual/temp_data\sub-Plimbo\ses-001_date-2025-12-11T17-45-39\SessionSchema\SessionSchema.json None false