From 01e1ccd3eae73dc38784163e0475aa6fddc00883 Mon Sep 17 00:00:00 2001 From: Skorinn <42702903+Skorinn@users.noreply.github.com> Date: Sat, 5 Sep 2026 12:04:25 -0500 Subject: [PATCH 1/4] Say where the external libraries come from The README described CommonControls.dll and DeviceInterfaces.dll only as binaries with no source here, which left a reader with no way of finding out what they are or what they may be used for. They are built from repositories of their own, which the README now points at. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0153VVkWg7DQmdaNLcvtY37w --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00409f4..bad82e3 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,11 @@ The coding standards are written down and are followed throughout: - [Math.NET Numerics](https://numerics.mathdotnet.com/) (MIT), for the statistics - [Moq](https://github.com/devlooped/moq) and MSTest, for the tests -- `Externals/CommonControls.dll` and `Externals/DeviceInterfaces.dll`, committed binaries with no source - in this repository +- `Externals/CommonControls.dll` and `Externals/DeviceInterfaces.dll`, built from + [CommonControls](https://github.com/Skorinn/CommonControls) and + [DeviceInterfaces](https://github.com/Skorinn/DeviceInterfaces). They are committed here as binaries + rather than built alongside this solution, and the terms they are offered under are stated in their own + repositories. - `TruRNGpro/rng.h`, a third-party header that wraps the serial port setup and reads for the device. It is not covered by this project's licence. Its author states in the file that it carries "No copyright, no warranties", which is a disclaimer rather than a formal grant such as CC0, so no particular legal status From 6916b6eeb4a1784fd11476141ba0ff6c20392c5b Mon Sep 17 00:00:00 2001 From: Skorinn <42702903+Skorinn@users.noreply.github.com> Date: Sat, 5 Sep 2026 12:07:54 -0500 Subject: [PATCH 2/4] State that the external libraries are MIT as well They are, and saying so is more use to a reader than pointing them elsewhere to find out. An earlier check of those repositories reported no licence, which was a fault in how the check read the answer rather than anything about the repositories. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0153VVkWg7DQmdaNLcvtY37w --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bad82e3..b468828 100644 --- a/README.md +++ b/README.md @@ -142,11 +142,10 @@ The coding standards are written down and are followed throughout: - [Math.NET Numerics](https://numerics.mathdotnet.com/) (MIT), for the statistics - [Moq](https://github.com/devlooped/moq) and MSTest, for the tests -- `Externals/CommonControls.dll` and `Externals/DeviceInterfaces.dll`, built from +- `Externals/CommonControls.dll` and `Externals/DeviceInterfaces.dll` (both MIT), built from [CommonControls](https://github.com/Skorinn/CommonControls) and [DeviceInterfaces](https://github.com/Skorinn/DeviceInterfaces). They are committed here as binaries - rather than built alongside this solution, and the terms they are offered under are stated in their own - repositories. + rather than built alongside this solution. - `TruRNGpro/rng.h`, a third-party header that wraps the serial port setup and reads for the device. It is not covered by this project's licence. Its author states in the file that it carries "No copyright, no warranties", which is a disclaimer rather than a formal grant such as CC0, so no particular legal status From e2dfd5e224d9144f8a0af2aa5366ed181dc348da Mon Sep 17 00:00:00 2001 From: Skorinn <42702903+Skorinn@users.noreply.github.com> Date: Sat, 5 Sep 2026 12:12:56 -0500 Subject: [PATCH 3/4] Bring CLAUDE.md back in line with the code It was written before the last four merges and had drifted: - It said the test project copies the native library into its output, which was the state of things when the application could not load it at all. Both managed projects copy it now, and the reason is worth stating. - It said the statistics are recomputed for every point. That holds while recording but not while loading, where they are worked out once per batch, which is what stops a large file taking time that grows with its size squared. The cost of the recording path and the reason rolling sums were not used are recorded as well. - Its description of preparing a file for appending covered two of the three shapes that are handled and described the whole file as being rewritten, which is no longer how it works. Recovering a file that was left unfinished was not mentioned at all. - Nothing about the licence, the release workflow, or that the unit tests do not build the form's event wiring, which is why the crash on loading a file went unseen by them. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0153VVkWg7DQmdaNLcvtY37w --- CLAUDE.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6b074ac..dc8e537 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,9 +36,10 @@ nuget restore RandomNumberGenerator.sln & $vstest "RandomNumberGenerator.Test\bin\Debug\Random Number Generator.Test.dll" /TestCaseFilter:"TestCategory=Component" ``` -Build ordering matters: post-build events copy the app output to `\bin\`, and the test project copies -`\bin\TruRNGpro.dll` into its own output. Build the whole solution (not just the test project) or any -test that reaches the P/Invoke layer will fail to load the native DLL. +Build ordering matters. The native project copies `TruRNGpro.dll` to `\bin\`, and both the app and the +test project copy it from there into their own output, because a native DLL is looked for beside the +executable that loads it. Build the whole solution — building either managed project alone leaves the DLL +missing and anything reaching the P/Invoke layer fails with `DllNotFoundException`. App output: `bin\Debug\Random Number Generator.exe` (also copied to `\bin\`). @@ -73,21 +74,44 @@ sentinel. ### Session data model `RNGSessionData` keeps a bounded `ConcurrentQueue` (`DataWindowSize`, default 6144, overwritten from -`RNGChart.MaxDataSize` at form construction). Stats (average, mean deviation, standard deviation) are -recomputed on every point; the queue is walked under `m_DataLock` even though it is concurrent, and scalars -are published with `Interlocked.Exchange`. +`RNGChart.MaxDataSize` — 1,024,000 — at form construction). Stats (average, mean deviation, standard +deviation) are recomputed by walking the whole window: once per point while recording, but only once per +batch in `LoadDataPointsBatch`, which is what keeps loading a large file from taking time that grows with +the square of its size. The queue is walked under `m_DataLock` even though it is concurrent, and scalars are +published with `Interlocked.Exchange`. + +Recording therefore costs a walk of the window per reading, reaching about 10% of the 100 ms interval once +the window is full — around 28 hours of recording. Removing that needs rolling sums, which change the +computed values and are deliberately not done; `MaxPoint`/`MinPoint` return `double.NaN` when there is no +data rather than throwing. ### File format and append semantics Session files are XML: a single `` element containing `0.123` children. Element/attribute names live in `XMLConstants` — use them rather than string literals. +Values are written and parsed with the invariant culture, so a file written where the decimal separator is a +comma reads back as the same numbers elsewhere. `Target="-1"` means no target; `TargetValues.NO_VALUE_SET` +(-2) is internal and is never written to a file. + Loading an existing file (`RNGSessionData.LoadSession` → `RNGSessionDataFile.LoadSession` → `RNGXMLReader.LoadFile`) streams data points in batches through `LoadDataPointsBatch`, which fires the same `DataPointAddedCallback` so the chart fills in as it loads. Afterwards `RNGXMLWriter.PrepareForAppend` -rewrites the file **textually** — truncating at the last ``, or converting a self-closing -`` into an open tag — so writing can resume. Changes to the session element shape must keep -that text surgery in sync. +edits the file **textually** so writing can resume, handling three shapes: + +1. a closed session, where the closing tag and the whitespace before it are truncated away; +2. an empty session that closed itself, where `` is reopened; +3. a session left unterminated by the application stopping, where anything after the last complete + `` is discarded so a half-written element cannot be appended to. + +Only the ends of the file are read rather than the whole of it, because these files grow by roughly 2MB an +hour. Changes to the session element shape must keep that text surgery in sync. + +A file of the third shape is what an interrupted session leaves, and it is recovered rather than rejected: +the reader keeps every complete data point, refuses a partially written one, and reports the recovery +through `LastError`, which the form shows to the user. A file that closes its session and still fails to +parse is damaged rather than unfinished and is rejected as before. The two are told apart by the shape of +the file, not by the parse error. ### GUI `GeneratorForm` is a state machine over `RngGuiStates` (Idle / Running / Paused / Terminating); the @@ -126,7 +150,10 @@ here (`DeviceInterfaces` supplies the `USBDeviceNotification` constants used in `TruRNGpro/CODING_GUIDELINES_CPP.md` (native) are authoritative and are actually followed throughout. The non-obvious rules new code is expected to match: -- Standardized file header block on every file (name, description, copyright, revision history). +- Standardized file header block on every file: name, description, copyright, the MIT licence pointer, and + revision history. The templates in the three guidelines documents are the ones to copy. The name in the + header has to match the file it is in; two headers naming the file they were copied from have already had + to be corrected. - Modified Hungarian notation: `m_` + type char for members (`m_sFilePath`, `m_iPort`, `m_bValid`, `m_Timer` for objects), and `s`/`i`/`f`/`b` prefixes on locals. - `#region` blocks in a fixed order: Type definitions, Constructors, Event Handlers, Methods, Properties, @@ -141,3 +168,20 @@ non-obvious rules new code is expected to match: swallowing and returning `false`; the form catches them and renders via `SetStatusBoxError`. - Tests: `[ClassName]Tests`, `[Method]_[Scenario]_[ExpectedResult]`, banner-commented Arrange/Act/Assert sections, Moq for collaborators, `[TestCategory("Component")]`. + +## Licence + +MIT, in `LICENSE`. Every source file carries the copyright line and a pointer to it. `TruRNGpro/rng.h` is +third-party and is not covered by it; leave its header alone. + +## Releasing + +`.github/workflows/release.yml`, started by hand from the Actions tab and from `master` only. It refuses to +run unless `RELEASE` is typed into the confirmation box, the version reads like `1.2.3` and has not been +released before; it stops if the tests fail or the package is missing the application or the native +library. What it produces is a **draft** release, so nothing is tagged or published without someone +pressing publish. + +The unit tests do not build the form's event wiring, so a fault in it passes them: the crash that made +loading a session file impossible was only found by driving the built application. Worth doing for changes +that touch `GeneratorForm`. From 35419088adc6856161404537668308fc2b0915de Mon Sep 17 00:00:00 2001 From: Skorinn <42702903+Skorinn@users.noreply.github.com> Date: Sat, 5 Sep 2026 19:30:26 -0500 Subject: [PATCH 4/4] Take the external libraries from their releases and bring their docs along DeviceInterfaces moves from 1.0.0 to the 1.1.0 release. CommonControls is taken from its 1.0.0 release, replacing a 1.0.1 build that was not published anywhere. Reading the public members out of both of those shows the same 30, so nothing is given up by using the released one, and it is a build that can be obtained again rather than one that only existed here. The documentation each release ships with is committed beside its library, so their members describe themselves while writing code against them. MSBuild picks these up on its own and copies them to the output; the release workflow already leaves documentation out of what it packages, as it is not needed to run anything. The reference in the project file follows CommonControls down to 1.0.0. Checked with the libraries in place: the solution builds clean, the 207 unit tests pass, and the application was driven through recording, pausing, stopping, clearing, loading, appending, the analysis comparison and recovering an interrupted session, at 38 and 13 checks with nothing failing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0153VVkWg7DQmdaNLcvtY37w --- Externals/CommonControls.dll | Bin 8704 -> 8704 bytes Externals/CommonControls.xml | 91 +++++++++++++++++++++++++++++++++ Externals/DeviceInterfaces.dll | Bin 5120 -> 6144 bytes Externals/DeviceInterfaces.xml | 47 +++++++++++++++++ RandomNumberGenerator.csproj | 2 +- 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 Externals/CommonControls.xml create mode 100644 Externals/DeviceInterfaces.xml diff --git a/Externals/CommonControls.dll b/Externals/CommonControls.dll index 67018a0af5627a0ce99bdd15d344828277589686..22b9845ab98b1f07e8c457f0fea17569db2667fb 100644 GIT binary patch delta 2303 zcmai$ZEO@p7{{NP-I=|+9!Gau+Upy=(o*(HTdvSrZ7hOU#fw3$TJeNNFA=pW-AK`B zid;bvi)g5$h%tUp5>080(TZsN;!BMpABfS25MF$LiHVAehD6c-%(c;P&Sih|pZ_y6 z&+hEZ-Hr5&^z3}l&h6j1Z)`_3Z|WS|drj<9`&C-kF0QQ@s)h<`fTpUHMZY3HlDJkd7gl9Z3mRP- z;eJAQt<#tXaKB!i;^nMCg{>v|`EW{XXUNoo4?9TNQe2qEwZlOp;tQKx8zV<97+Rov z%ju{L2R_;-qONRqTp1+MI5r&QsZmI{b}FtP~5HG`XGJ4arrijU3(E1IhmS{8et9PA~`PuS43Zu>Fcg(=31TX9V z8KyG9$OR=;fDuZ9#nfhap5b!CjA5@~)-VUop#h`s$eZdO!_9_A3_mwK1y+(~9Wkbd zxnLuW(Qdm1?B<%j)NBJs(GxesK4pvP0CX30g}scPq-wE>lFVWKaDEUsf+2A$oIDo+ z8Y}5e3|!fqf6X4!s3>tG%6;voM#?E`JpLKNA!5mPzmo_#&J~>&&Mo!<;~ao<6k1VOd~clZ;fz2( zMSDb;?V&>~%qHltxLExa>EI<&wHMu3r1wjq>{EM+<}aDTPAQz~X`4Q#v_9nKq|bN< z=_BJ=`V>!|DXO(*Wd?5w*00NL7zhS)w@>~ZtFnq$pK*RWx#aDhz2A0k`g-0uKdNT0 zHh*W#PB2z20e568W^pr4fw>KJf9Lvpx z+_maguinpC(v5OyZpvGUgkGP{+N%*YsiU@!yHHC@PDfL%`Ui!PXlqSPwwHcr&DphA z?$B$dzw55UwGsaeHmaI<{esI9_n*x@|L)3dZGRsAdH7(B{s!#nT9aFo4%V#;2Ghad zhV{YiL#jW%$UBQsGbOJ7KqSWM%6Ozxy&Ct`vG_MCR=IT6l>UyLE>kL7*`ZH9wkC>t zqB4h{?=zM4j3#TU7BZHa%v9gTtQ1MLzb2`^s%aM~YNgbqoA{1}@f7&>rshsGe_~@P F=O5GpZzcc$ delta 2294 zcmai$eQXp(7{=e3o!z~=9!Gck{c-IbZDIQXv`~r^P%16uVu~%K#WOZq18Oz#q@^mU zdIuV$f@0bDK`@9>G!=VTpRh%z07Z(dES|M zcV}krE>WMTe|C$waO#Qi{%!gETuuL*ZGmkyL^l(zB}B!dKb+>jgUFAzK;Swz%q!d| zo53$+x|`T^R_qS2)j3BIlGkz+PC7kOM6|^wVs2H=e7Bhg;u%D~M;RvJPNtMxN#$z) zxkAY-*+Ew_TS`O897rBY4ROkHa-}YuQ<&482NlZ)%JM2Tx{UltVrj7;tlU@usC2rI zJB6}qNFoeyXPWyv&tgTcSDPCzhLa}DZdL2N*F?&hsmW<9;f<9bUS4L2X+((|x~I$D z#zfTc#%7>>&TknNre(w&G|o)KbWf~Q#->?1VtO)W322ej-U>z%E9$dZ zW~NgPZF9qL2WHg!0OFJZA7!9$v&Pm-8L*?=_=km3&E1uY)LO}b?8Q*dTFdu z8kT{^lS{90VpZ^wmC~?uH?4rcR#-M=W~=<%!7lnLvTaIc<&M0=Xx1&x9Mt6=o3fQk z`gze5o13P(VZ+m1;f{#R_*|KwfcJ7NXIU-RCboUjmOHQP* zc{C<%c{-GxJSLO2+`)9^OU0cD->G&V)}6)a=v?#|HWPKe)T`Eab>4q>H`ah&!eK{d zDr#Ltd!SRLZfuRzqkaeN)9zV)MiAR9my3KTX(%3)Y7{&wmAGLe9N!^u6mc2Utj*I6 z8c}TJnm(0U@eBq%&yNU$=4z7Zimxd?pxCGQvFfvlXJUp%#lMtuj%&h4@iHj=%{LaLf`3^(E0qpb;z`AC6-U8bk_^*0m_c4P zCZ*+IG^fk!1Ll>IA=r* zI^IM*7+ePqU|0-+h}b{}pf^F^E}o(V%*zdEi|@dIL;5}%Rta{3Z^OAq?4wtp4e%0^ ztfhnC&(OnEgw?cI5Hr*r}zuC!Ie|F7AN{_FuwWaZw@yO9Vpn#hL@cu5n{}vg^1}8mL;1332uZqv)3t| zZ^&3;BWAQ3O!!vfX|AL8c+{wlRMWH?@M*GJ>{fiDdWh^qe{$G!s?jGxbgR{%`ZpjS z_sIDZJM9)ZA%tJmQM+X#U^PuOO^?-Xby!VFVw6W^hi8T~W;OZ!rm0!&9;?GGPb+d7 zG{f?9L*0}eK|;F)u$m$VkRaxLX|TrjsL zt=W;|82^+|h*Af`1!c?)Gu&Pz r|30QwlgB+6EM-;h+2DM=ie^#epcT5ny!i3Mmp1)SgXV9nzta2{S8;(A diff --git a/Externals/CommonControls.xml b/Externals/CommonControls.xml new file mode 100644 index 0000000..cff9196 --- /dev/null +++ b/Externals/CommonControls.xml @@ -0,0 +1,91 @@ + + + + CommonControls + + + + + Toggle Button control + + + + + Drawing styles for the control + + + + + The background of the control is filled + + + + + Only the outline of the background is drawn + + + + + Default constructor + + + + + Override for the paint event + + IN - The paint event arguments + + + + Creates the path for the control + + The rounded path used to draw the background + + + + Gets the rectangle for the toggle + + The bounding rectangle of the toggle for the current state + + + + Background color when the toggle is in the OFF state + + + + + Toggle color when the toggle is in the OFF state + + + + + Background color when the toggle is in the ON state + + + + + Toggle color when the toggle is in the ON state + + + + + Background color when the control is disabled + + + + + Toggle color when the control is disabled + + + + + Drawing style for the control + + + + + Override for the Text property to remove set + + + + diff --git a/Externals/DeviceInterfaces.dll b/Externals/DeviceInterfaces.dll index 23760ce25e817d2494627369a861f9d5d9e647ab..5814f98e7b17f371a70dd457372b43b997dd8a31 100644 GIT binary patch delta 1859 zcmY+FUrZEN6vn?hvn;T?F1x6>irN9JARt|!38mGFB8&f4F^C}2mK6rv?6SMe4k9&? zMVq!ZCUtXLjrvs6G}SaFhNke)n6xjAO=C1M=|dZ%O`Gq?Z>Xh5A-SXLuA$%`jT0Z+|RqTXAxj3&FBczenH!or#|dOsJ} zLIj*CJ5gt$&{?8i``;*0t2r~e*Vk^dIAbQef}?WG&Q&Y#XzMmF(}Aj!K#q2p$yasT zRy;MDbtd|i)9sgh(9#o;=r>g?IMowkkf9oeW?FK0sFV0hUI*qoiOkYjrJG}i^! zWOB}RMRR=T{&f*_xlyh;`~|o55;SsUH#}mO?nYkGQq(j@xvl))6)O~<;tBP8iako% zBqEv{5!W10!G$iv zxqTk@wD(xIKNh1W+Ac2rw~F$i%lcXMOa-E>kcJGC)6qs>;08wvTvI%cVtlL%1W7km zk{)cBrU@`lhpmcR6?ZD`S3IN`BbOtt{Bgx8#f#XYRpBBAwU@{%`pftP34Jr!=W3&J zT94o=dkXqvO#F-*Rm9Ep@nxDW!W%R`93cZ3C6^(I2A-62qG-yQ*2lIZq!ogR?1<;Aw;PpFiB?dj#z~YT14n5WhxK_*huTzm2(QT@T^%SUJqG5 z*6zP+AEuQ5yf&xKh|Y4qfLf+j3(PKYXSAA#WtuxWlj$R|B%(1Zdz7|&Dr=i#4ejZ% zOgd$z>}Yz}l&6cnYw3-rnwvU~51AP|o=&0Pw9?p{w&Nr5p_rpY;z`@I&}rIxVp)5k zw`HZ};o`OW&Xg@Dia+(HGjDZ_PsEaOdrFpjqRqEFJ*_k4=eNGwB7>(xAKrz_5TZyo zxMe!QpcDEXXhnstpfAKfdC9Y?RC9+4plP8H_h00K5Fz=cXS00j**(2Spi?bw44cQ| zL#7c=F|Cmpy-6#U$(UBw7)e`3!`_YqM#PGZnUiTN!Oq!)oz58SkzCvmKN$t8*p&in z+S8dSD?WPEHom=TG=-a*jc7b!8eJ2~q?rOX2&_BO+S1Y_k9w;`qdez55CVGd&fapK zhg1JSwmyC{`&&`g%QLSwMj-XWcYLR zEPIV`MvJxqd(nXd^dhQ{v&N*lm!LMho?(_p-lmkHurNtdEf%91r=CJqMoTWRpud&$ z>ThC=+!AQ5XhN9DHFHdqy%LztV~!aQwiGuqjj+>ZgQZ`HVq@W7 No^KPw$08pt{0H8BRz?5- delta 1368 zcmXw(T}+!*7{~wT99l|Sy0?WfJ{BykldpwTh(mC5A0PuAgM|;r)+|(3p>a@X%Mxxz zotil361$Tri^h-43%95-Vhk6~n8j$E$=t=VXf$SF7rS(7G7~S1|8s!TJiqfi|MT{J zp7ZtQWBG$IaD-gS$^(NoOH z#C|4?4&^ebnaQllb)cBWJ~}dlCHju>_mA{-v;TOb- zMUQmjA9aB&!0`H_?WW}=Q=KiuP~b)3;2n8h?X{7QQHoFWp_nC%=Dj#ZZnbPD59NC; zC&@>#9SKghLmnf)rlN4#@^!1PAZbz|k3#sqJUF$U)Pz2j=AI6Q&G4J}T;{k|U#v$r zpbr+$7SH=jukr**gYL!Maux3}LJ-E{ltnRmbGegAG@$8)>)oz#QA z>PT_13i>O9ezzlprxrzC5#-Ggl^%~Hl&D3e)bIIr6;TrUSKooHE_?2`2!y)QvkRHj z%!@${^=>E&C z+0fMwcD!U>*xeZ)m`QeLQYVw~?)1d`baEz_jd!JwC*wm?iA?giex#i2Y#x+_@YK HJP`i_k*Mn; diff --git a/Externals/DeviceInterfaces.xml b/Externals/DeviceInterfaces.xml new file mode 100644 index 0000000..8bfec1e --- /dev/null +++ b/Externals/DeviceInterfaces.xml @@ -0,0 +1,47 @@ + + + + DeviceInterfaces + + + + + Registers a window for notifications about USB devices being connected and removed + + + + + Structure for filling the notificationFilter parameter buffer for RegisterDeviceNotification + + + + + Registers a window to receive notifications when USB devices are connected or removed + + IN - Handle to the window to be notified + The notification handle, which must be passed to UnregisterUsbDeviceNotification + Thrown when the window could not be registered for device notifications + + + + Unregisters the window for USB device notifications + + IN - Notification handle returned by RegisterUsbDeviceNotification + + + + Value of wParam on a WM_DEVICECHANGE message reporting that a device was connected + + + + + Value of wParam on a WM_DEVICECHANGE message reporting that a device was removed + + + + + Window message raised when a device is connected or removed + + + + diff --git a/RandomNumberGenerator.csproj b/RandomNumberGenerator.csproj index f327200..d658283 100644 --- a/RandomNumberGenerator.csproj +++ b/RandomNumberGenerator.csproj @@ -53,7 +53,7 @@ RandomNumberGenerator.Program - + False Externals\CommonControls.dll