UniScan
- 35 Devlogs
- 159 Total hours
Modular scanner/radio client and multicast server. Plug in a scanner, set it up in the server software, and open it up to the internet for others to listen to, or control it yourself by logging into an admin user.
Modular scanner/radio client and multicast server. Plug in a scanner, set it up in the server software, and open it up to the internet for others to listen to, or control it yourself by logging into an admin user.
Sorry, haven’t been super active due to life.
Fixed a few bugs in the meantime and implemented most of the SSR binding stuff, but it’s far from finished, hell I haven’t even gotten to test it yet.
Will be gone for a bit tomorrow, and the next day I will be at a different house for a few days albeit with my computer.
While I didn’t get to scanner state displaying yet, I did refactor the classes that handle how remotes connect, disconnect, etc so that it’s not spread around the codebase.
This fixes a few bugs related to state management due to how originally these viewmodels were constructed separately of eachother.
Tomorrow I will be gone for a few hrs but should be back soon enough to hopefully make some more refactors and/or start on the remote display.
Pictured is the new disconnect button in the remote on the sidebar
The client can now disconnect itself, and more styling has been added to the SSR UI (shown here is a Container which is centered and has a slight margin)
tomorrow will be some more work on the SSR UI and then finally getting the scanner state to show on the client.
I’m not too pleased with the idea that the server may change the look of the device pages on the clients, though… I still feel like there could be some better way to go about this.
I FINALLY GOT SSR WORKING MY GOD
still too primitive to do anything atm, working on it.
A friend of mine was able to test it on iOS, we got it working.
Gonna have them send in the PR later and set up CI for it.
Started working on state sync, will continue tomorrow probably
States are synced now but I need to think about how to deal with scanner groups and channels and such
because there are different types of channels (such as P25, conventional, etc) among other type issues
and I want to reduce the need for any modules on the client if I can.
Lots of changes, most of them are backend changes, but there are also a handful of frontend changes too!
https://github.com/UniScanProject/UniScan/releases/tag/UniScan.Client.App%2F0.1.0
Remotes are now stored per file instead of one large file, and the UI has been neatened up.
The server now sends metadata containing a logo, various information, an MOTD, links, etc.
The UI has been updated to display this information too.
There are more changes than this, but there are too many to list here without hitting the 4000 char limit.
Attached is a small video showing some of the new UI stuffs.
By no means is it bug free, but the PR reached a point where it could be merged to allow for other important tasks to be focused on (like y’know, showing the actual scanner info)
I think this looks pretty nice
apparently Avalonia has some nuget package that adds ItemsRepeater which does what I was originally trying to do in a cleaner way
Here’s the announcements UI, I think I’m almost done on this branch and then I’ll continue on main.
Need opinions on this UI
The server logo now shows where possible, so on the home view and in the sidebar
In the remote view, there is a top ‘window’(?) that holds (from top to bottom): logo (to the side: title, description), server links
and then the bottom floating window thing holds the server MOTD
I still need to find a place to hold the server announcements…
Alongside this were a ton of internal refactors, and the subscribe button now works (but the server still won’t properly send the current state)
Been trying to post this since yesterday (I re-recorded the video today), but Hackatime has been down.
Remotes are now stored per file instead of all inside one file.
Additionally, server info is now properly cached!
I had to rewrite most of the remote initialization flow, but now it’s much better.
Adding and removing remotes automatically saves to disk because I can observe the list.
This isn’t the end of this overhaul, but we’re getting closer… I think I want to refactor more of UniScanClient next.
This is happening on another branch: https://github.com/UniScanProject/UniScan/tree/feat/use_server_provided_display_names
After this branch is merged into main, I will make the available server
info also show since we have the entire RemoteInfo being sent, which
includes announcements, a description, a logo, etc.
Working on dropping display names from client remotes
As well as some refactor of the connection flow using my new TaskPipeline
Once this is done, the server will be able to send branding, announcements, and other useful information on connect for the client to display!
Just released a new version, now you can make remotes from the UI finally.
Here’s a video showing the creation flow.
Note that the list is dynamic based on classes implementing IConnectionMethodFactoryViewModel with attribute ConnectionMethodFactoryViewModelAttribute
working on fixing the fact that you can’t really make remotes
FINALLY I GOT IT DONE
IM SO TIRED
New loading screen!!!!
And OPFS file handles are now keyed by int
im going to sleep now
https://uniscan.dexrn.me
Much nicer loading screen
Alright I’ve committed and now you can access UniScan through https://uniscan.dexrn.me
Note that since this is still an early version, you can’t really do much.
You can also install it as a PWA on iOS, Android, and most desktop browsers.
(pictured is UniScan as a PWA in ungoogled-chromium)
Hopefully tomorrow I will fix the crash with adding remotes by making some sort of map of connection type -> UI widget to define the settings for them.
(meant to post this yesterday, but GH was still working on some cert garbage)
UniScan now works in browser!
I’ve also added a nice crash handler since unhandled exceptions act weird in the browser.
Just polishing up now, will commit and set up Actions to host this.
Once it’s up, the link will be https://uniscan.dexrn.me
(The crash shown in the video is a real issue and will be fixed later.)
FINALLY I GOT OPFS WORKING GG
OK I GOT IT SOMEWHAT WORKING IN BROWSER FINALLY
still need to keep implementing OPFS stuff though.
serilog 01:32:29 [ERR | UniScan.Client.Core.Config.Remote.RemotesListFile] Failed to save remotes to remotes.json
System.Exception: Entry not found
at SpawnDev.SpawnJS.SpawnJSRuntime.<_InteropCallApplyAsync>d__132`1[[SpawnDev.SpawnJS.JSObjects.FileSystemSyncAccessHandle, SpawnDev.SpawnJS, Version=2.1.8.0, Culture=neutral, PublicKeyToken=null]].MoveNext()
I think this looks pretty good for a little loading screen.
I’ll probably add a try/catch if possible and show an error dialog when some fatal exception is thrown…
The “Loading…” text is temporary and is done in Avalonia, will make a proper loading screen there sometime later.
Using this time to test out M3 web (even though it’s in maintenance mode)
This is just for the loading screen while it downloads all the WASMs.
The cell tower is temporary until I can either make my own logo or find someone who will make one for me.
Waiting on a bug to be fixed in SpawnJS.WebWorkers and then hopefully next update will include web support + a website for y’all to test the client!!!
Couldn’t do much today since I was so busy, however since I’m home now I can work on UniScan at a better time than 12 in the morning.
Didn’t get to release last night, as a new bug popped up.
11>MessagePack.GeneratedMessagePackResolver.g.cs(74,74): Error CS0426 : The type name 'Shiki' does not exist in the type 'GeneratedMessagePackResolver'11>------- Finished building project: UniScan.Network. Succeeded: False. Errors: 1.
Was able to fix it with some property in the AssemblyInfo.
(see: github.com/MessagePack-CSharp/MessagePack-CSharp/issues/2133)
I changed the Identifier to be a Slug instead for the devices.
Working on browser stuff real quick and will commit sometime soon.
Not fully happy with the manner in which this works, but the virtual scanner does work!
Will publish some if not all of it tonight, but no version tag for this yet until I fix my refactoring.
Working on a virtual scanner module to help development by simulating a scanner, hope to have it ready by tomorrow.
This also means any future contributors will be able to start contributing even if they do not own a scanner!
spun up entire android emulator just to realize it was some debug code I forgot to remove that was causing it to throw and die
because afaik currentdirectory is / for android apps?
gonna release this and then start working on remote adding from UI I think
UniScan source is now up, you can view it at https://github.com/UniScanProject/UniScan
https://github.com/UniScanProject/UniScan/releases/tag/UniScan.Client.App%2F0.0.2
https://github.com/UniScanProject/UniScan/releases/tag/UniScan.Server.Host%2F0.0.2
PacketRegistry
RemoteFactory.Create to pass PacketRegistry instance from DIRemoteServer constructors to take an instance of PacketRegistry
InternalUniScanClientPacketConfigurator
AssembliesPacketSource
InternalUniScanClientModule no longer adds an IPacketConfigurator to DI as this class has been removed.ScannerInfoPacket -> ScannerRegistrationPacket
UniScan:packet/clientbound/device/registration
BuiltinPacketRegistrar
IPacketConfigurator
PacketRegistry and RegistryPacketAttribute to UniScan.Network.Registry
IPacketSource and AssembliesPacketSource
AssembliesPacketSource collects all classes that inherit IPacket and have the RegistryPacket attribute.PacketRegistry
PacketRegistry.RegisterFromSource and PacketRegistry.RegisterFromSource<TPacketSource>
PacketRegistry.RegisterFromSource takes a given IPacketSource and registers all the packets it provides.PacketRegistry.RegisterFromSource<TPacketSource> creates a new instance of the given TPacketSource type and passes it to PacketRegistry.RegisterFromSource
SubscribePacketHandler now sends the newly renamed ScannerRegistrationPacket
PacketRegistry.RegisterFromSource
InternalUniScanServerModule no longer adds an IPacketConfigurator to DI as this class has been removed.AssembliesPacketSource
PacketRegistry and passes it to the UniScanServer constructor.PacketRegistry and passes it to the ClientSocket constructor.Been working on cleaning up the codebase in preparation for the initial commit. (thanks @CT5 for some of the motivation)
https://github.com/UniScanProject/UniScan will be the project’s home.
This does NOT mean it is ready to use, it is far from so. However, it means I will be able to publish releases and such, and everyone will be able to read the code.
I will also be working on a Module alongside the core project, which acts as a virtual scanner, for testing of course.
My next steps will be getting the virtual scanner module good enough to use as testing, then figure out what to do about the UI, as I don’t really want to force modules onto users.
My idea here is that we send a UI ‘definition’(?) to the client for it to render, and let modules hook into them.
Means we can also have each button call to the server when pressed if it’s meant to perform a server-side action.
I am also planning a scope increase to include SDRs and two-way radios in the distant future.
Would like y’alls opinions.
Doing better at Avalonia so I was able to fix some issues and make the Home screen actually contain something useful, with this being a list of all connected remotes and their devices in one place.
Oh, and a ton of netcode setup which I forgot to post in this devlog, will show it in the next one.
so far so good!
I might want to make the Identifier text slightly smaller…..
Now onto making “subscriptions” work, couldn’t come up with a better name than this though.
It takes me so long to do anything because I constantly have the urge to refactor things, as well as trying to get stuff perfect the first time.
FINALLY HOLY SHIT WHY IS IT SUCH PAIN TO DO ASYNC INIT IN AVALONIA
I wouldnt need to do this if the browser wasnt so picky with OPFS and async………. UGH
basically now during OnFrameworkInitializationCompleted I just run task in background that calls my async function
at end of async function it sets Content of root view to newly created MainViewModel
Finally getting remotes closer to working
Now you can add them from the GUI (but you can’t connect to them yet…)
didn’t notice at the time but I was writing out the length with the wrong endianness
fixed it and now dotnetty doesn’t silently ignore it
testing the socket, seems like packets aren’t being handled just yet but at least we’re getting somewhere