Yukon design megathread

I think this striped table layout is much better.
I also opened another PR at github for some discussion:
Current (possibly final format).

Need to work on some tiny non functional visual requirements, tab navigation and tests of course.

PS: Yay I’m part of the team

1 Like

This looks quite solid. There are some minor things to bikeshed, such as unnecessary capitalization (Sort On, With Order), unnecessary word “List” in “Online Nodes List”, and British spelling (INITIALISATION?), but they are probably not important enough to focus on right now.

Yukon related dev-call contents:
Start working on:

  • pyuavcan node reconfiguration options (target url, debug node id on the bus)
  • node details by id tab
  • documentation for these

*Postpone actual development of the python backend till Pavel finishes pyuavcan work, but keep stubbing out the backend api in modules, providing at least some mock responses, so that we are ready to rapidly integrate pyuavcan when the time comes.

I’ve started working a bit on the node detail UI:

It seems to me that it needs to have these 3 main sub-components:

  • Node/Constant information: Stuff like the crc64, hardware version, etc.
  • Controls panel: shutdown, start firmware update
  • Parameter list

The parameter list, upon clicking, pops up a modal which the user reconfigures the parameter on the fly, based on it’s type.

Regarding the shutdown and start firmware update controls, what should happen? One thing would be to have a confirmation dialogue for accidental presses, and then …? Do you poll for the node status on the clientside? How much time is a node firmwar eupdate supposed to take? Do I need to add some extra dialogue to pick a file (or whatever) before the update starts?

PS. I made a new component for the auto-copyable text – so most of the things will copy their contents to clipboard on click (ids, crc, uid, names, … )

@pavel.kirienko Forgot to tag you about these questions

I am thinking that putting the parameter list into the node detail view, as I made in the original gooey tool, was a mistake. We also need to keep in mind that we don’t really have “parameters” per se in UAVCAN v1.0; instead we have “registers”, which are slightly different. The details are doc’ed here: https://github.com/UAVCAN/dsdl/blob/uavcan-v1.0/uavcan/register/384.Access.0.1.uavcan

I described another approach which should work much better here: GUI Tool – Next Generation - #2 by pavel.kirienko (look for “Multi-node concurrent configuration tool”). Basically, it should be like a large table; the horizontal dimension represents nodes (i.e., one column per node), and the vertical dimension represents parameters. Ideally, the parameters should be grouped into a tree-like structure, like this:

Except that, on the screenshot, we’re dealing with only one device; in order to free up the horizontal dimension for other nodes, various register metadata will have to be collapsed into one rich cell: the current value, minimum/maximum/default, data type, edit/reset buttons, flags (editable/persistent), etc. Notice how the tree branches can be expanded and collapsed, this is very convenient. The exact UX/presentation is a matter of discussion, suggestions are highly welcome.

A service request of type uavcan.node.ExecuteCommand should be emitted: https://github.com/UAVCAN/dsdl/blob/uavcan-v1.0/uavcan/node/435.ExecuteCommand.0.1.uavcan

There also should be buttons for other commands, and an option for invoking vendor-specific commands; i.e., with arbitrary user-specified code.

The frontend should not care about the time it takes to update a firmware (for the sake of a general reference, a regular node with ~400 KB large firmware takes about 2 minutes to update itself over CAN 2.0B). The only thing we need the frontend to do is to ask the user for the location of the file, then make this file available to the backend (e.g. upload), and then make the backend call the service. When this is done, the frontend can safely forget the context. See chapter 5 for more info about this process. Nodes may emit progress information while update is ongoing by publishing messages of type uavcan.diagnostic.Record.

I like the tree view. How big can the maximum depth be? (For UI purposes)

You can have multiple browser windows open at any time. That said, I don’t think it’s needed to have support for many nodes on that tree.

How are the parameters going to be edited? Inline or with a popup window?

Also, do you want to have it update the parameters on edit or as batch? -> Keep track of changes and apply all together?

What format are the params going to be saved/exported in?

I would say, anything more than 4 levels would be impractical.

The use case when a user needs to bulk configure multiple nodes or compare their settings is very common in UAVCAN. I am not sure how it can be adequately supported with the old paradigm implemented in the UAVCAN GUI tool where the settings of each node were displayed in a separate window. The old GUI tool also permits the user to have multiple windows open, but that doesn’t really help; I think the old design was a mistake.

The exact user story of the new multi-node configuration tool still needs to be worked out. Perhaps the user should be allowed to pick which nodes among the available they want to configure, then the GUI will discover their registers and display them on the table?

Inline might work well for simple scalar types like booleans, integers, and floats. Strings and arrays will likely require a popup. Or we could use popup everywhere for consistency and simplicity reasons.

The screenshot which I posted above is of Zubax Kucher: https://github.com/Zubax/kucher. It supports array parameter editing by rendering them as text when the editor is invoked, and parsing the edited text array back into its internal representation when the user finishes their edits. This approach is probably suboptimal though.

We have to invoke one service request per parameter, the protocol does not support batch editing, so it makes sense to reflect this underlying model in the UI.

I think CSV should certainly be supported because it’s so ubiquitous. JSON would also be cool to have because it is well-supported by different software products, e.g., Wolfram Mathematica. The JSON schema should probably resemble the definition of the uavcan.register.Access data type.

So, the batch configure is supposed to happen on nodes of the same ‘type’? So, we are going to have let’s say 4 ESCs and display their setting ‘X’ on a single or 4 different rows, on the concurrent configuration tool?

The tree depth does not really matter that much, but I’m just asking for design considerations (inline margin-left’s would be dynamic based on depth, after all).

Shall I change the endpoints of the backend from /nodes/:nodeId/parameters/… to …/registers/… ? Also, on the register definition posted above, I see no min/max or default setting, just value. Are these obtained separately? (I have to update the current spec to match the extra timestamp, persistent/etc states.) Reading the values back on each request really helps for automatic error checking. Same thing for register type. Parameters had some real/integer/boolean type. Where are the register ones?

They don’t necessarily have to be of the same type; for example, many different nodes may have a register named can.bit_rate, for example. We are probably going to encourage node designers to follow particular register naming pattern to ensure good coherence between different vendors.

Yes. I should have been more explicit about that probably from the start. But then again, it’s explained in the current spec draft. :wink:

See the docs for uavcan.register.Access:

For example, suppose we have a register named uavcan.node_id. Its min, max, and default values will be contained inside three other registers named uavcan.node_id>, uavcan.node_id<, and uavcan.node_id=, respectively. These metadata registers do not need to be displayed for the user, since they can’t be changed anyway; they are to be used by the tool to figure out the relevant parameters of the target register. The Kucher application which I linked above deals with these metadata entries as follows (open the link please, the excerpt displayed on the forum is incomplete):

A register value is represented by uavcan.register.Value, which refers to https://github.com/UAVCAN/dsdl/tree/uavcan-v1.0/uavcan/primitive. Make sure you’ve read the DSDL doc comments.

I’m sorry, I must have ended up reading some wrong version of the spec of the draft in the process.
Here’s what I’m thinking about, regarding the Register list and editing UI:

UI Ideas:

  • Mutable registers only, show as clickable (blue anchor tag).
  • Persistency / Volatility show up in a different column named ‘Lifetime’ (or recommend something better). - -
  • Clicking them pops up the register update UI, which performs the request and gets the returned value back. If the returned value matches the expected one, the register update process is completed sucessfully.
  • Regarding the concurrent node configure, maybe we can have a checkbox that switches the view of available registers: based on selected nodes and/or selected register names. For example, you can type in some regex to match the nodes, and/or some regex (or text) for the parameter you want to change. These filters run independently but result in a set of nodes and parameters that are shown: Of course, as an error prevention feature, only the nodes that have the parameter listed are being contacted through the API. We can have some extra check on the popup register edit window that selects nodes and upon the press of the ‘Update Register(s)’ button, shows which nodes failed to accept the changes (did not report back new value).

API json responses format questions:

  • What’s the format to expose each individual’s register details? For example, do we send a list out of all the register names existing? (That means, each client needs to parse each individual one by name, decide which have min/max/default values based on $name[ <|>|= ]) Instead, we can do that on the backend and send the response in the current-ish format, which contains all the information for one register-details object, including the min/max/default values. The second one is my preferred way, which is more user-friendly on the client consuming the API. This does not mean that you are prohibited from querying each parameter by name: If you specify a name that ends with ><=, other parameters of that register (ex. current value, or the other min/max/default ones) are not queried.
  • How are the Primitives going to be exposed as a register’s data type? Instead of providing an enum of possible data types, we could have a field showing it’s object type [scalar, array, empty, string, unstructured] and if that object type is scalar or array, we can have an extra item type [bit, int natural, real] as well as a bit length type (not for bits) [2~64].

If we’re going to aggregate registers by name, the columns will be used for the node dimension, so there won’t be any free columns left for mutability/volatility (and possibly other flags which we may add in the future). I would instead recommend to display flags in the same cell as the parameter value (together with other parameter metadata). Different parameters sharing the same name may be mutable on one node and immutable on other nodes.

From the above indirectly follows that we probably shouldn’t hide any registers unless the user explicitly requested that. For example, the set of filters that you described later might also include options for mutability and persistency.

Yes, this makes sense. It might also be useful to allow the user to multi-select several register cells on the same row and then click some button to bulk-edit them, but it can be done incrementally. Please keep in mind that in theory, some nodes may not support the standard service uavcan.node.GetInfo, which means that we won’t be able to know their names, and the only way to select them would be to specify their node ID manually. Although our experience with UAVCAN v0 seems to indicate that implementers tend to always support this service.

For example, a user may want to see registers that match the following filters:

  • the node name matches some_vendor.* (suppose this is a wildcard); this automatically removes all nodes whose name we don’t know, e.g. which do not support uavcan.node.GetInfo.
  • the register name matches uavcan.*.
  • show only persistent mutable registers (i.e., only configuration parameters).

Take my response with a grain of salt, but I would actually prefer the first way, because in that case you can implement the whole register thing without introducing any special logic on the backend side. You will merely need a generic service invocation API: the frontend tells the backend to invoke a service such-and-such on the node so-and-so; the backend does that and simply returns the response structure as-is. It’s up to you, but the first case seems simpler, provided that you are okay with implementing the register type checking and input validation on the frontend.

Maybe it’s better to find a generic way of representing DSDL objects in JSON, and then use that model everywhere, including the register management logic? I recall @scottdixon said that he made something to that end.

I’d take a look at visual studio code’s “peek” interaction as a possible solution for this.

This is interaction is halfway between an accordion and a popup. We could use a similar technique to expand out the parameters for a given cell.

While we’re listing deficiencies: one problem with the old design is that the UI often becomes unresponsive when attached to a very busy system. Remember that some views will be dealing with things that can number in the many hundreds of thousands and that can change at very high frequencies. Because of this it’s essential to avoid creating performance bottlenecks across the REST interface. The UI must deal with paged views into large datasets and the python server must provide ways of coalescing high frequency events before updating the state of the UI.

Again, let’s look at something like “peek”. I really don’t want a lot of modal dialogs in the UI.

Just to throw my random two cents in, using an abstract representation like JSON or msgpack would be great. The more you can leverage slow changing solid 3rd party components, the faster everything will go.

Also, the peek interaction would work nicely for things you only need to see rarely, like type or bit depth. There might be a way to expand that later if that’s useful.

That’s kindof what I mean, but how are you going to face the problem Pavel addressed: You might want to select an arbitrary number of parameters that accept the same data type and batch-edit them. I imagine some two-row UI: The top part contains the register tree view, with all the filtering, flags, etc and then on the bottom, is the ‘current register workset’ : the homogenous (same data type) selected registers to batch-edit along with their target nodes and some buttons: remove from the working set, batch update all, etc.

Individually editing by clicking an edit button on the tree or on the working set row below, can be like the vscode window, or inline, or with a popup. Batch editing would not need a popup since we have something showcasing what we are working with below.

EDIT: With many many nodes displayed, I’m not sure if it makes sense to have each one displayed on a separate column, but the batch editor is a must.

Filtering: The node list homepage is meant to be a very quick overview of what’s on the bus: filter supports regexes as well and matches against all the attributes of the current node list response [id, name, health, status, vendor code] and supports sorting based on these attributes, ascending or descending order.

On the batch register editor, I think it makes sense to change that a bit. Split the filter on flags and arbitrary text provided. The text matches by regex or by .contains() the register name. Aside from that, we can have flags like lifetime:PM (lifetime: persistent and mutable) or simply lt:P and other shortcuts like that to make this usable.

I’m not sure if it makes sense to keep the current ‘expanded’ node details UI I posted on github:

, as a means of quickly looking up parameters on a single node: I can add a filter and some auto refresh on a particular one, or all of them or whatever.

The crucial idea is that we take all registers from all nodes and join them into one view, so that the human developer can easily assess, compare, and modify all changeable and/or observable variables of the entire UAVCAN network from one place. There are huge possibilities for various semantic highlights and advanced features that can be added incrementally; for example, the user could ask the view to highlight rows that contain distinct (non-uniform) parameter values, or parameters that differ from their default values, etc.

Can we call this thing “Global Register View”?

There should be a way of viewing the node details (such as the CoA, version info, and so on) and issuing commands (reboot, shutdown, vendor-specific, etc). Viewing the node’s registers is optional since that function would overlap with the global register view. I’m not sure what’s the best way to implement that in the sense of UX.

Here’s a first prototype of the global register view (it’s dynamic, meaning It’s being generated from the data model). Some things are missing, like collapse and expand
are missing, but I’m adding these as we speak.

Some questions regarding the Global Register View

  • How are we going to show current register mutability, persistency, value, it’s data type, min/max (if they exist)?
  • Do you have anything in mind for select for batch edit / edit individually controls? Buttons, colors, etc ?
  • Shall I add another panel on the bottom, showing the selected commands for batch editing ?

I am starting to think about the implementation of plotter tool too. We are probably going to use a simple websocket to stream new data to the frontend (or whatever consuming client). Is the UI-UX going to be the same as the old gui-tool , or are we going to drastically redesign it like the register view?

Yesterdays notes on the devcall (forgot to post):

  • Start the global register view which is supposed to be a view on all the bus variability.
  • Postpone the design of the register types on the API side
  • Rename every ‘parameter’ thing to ‘register’
  • Things are in a highly WIP state, focus on prototyping, write tests upon each feature completion
  • Research a bit on third party component registration and dynamic integration

P.S. UAVCAN is the most important and challenging computer engineering thing I’ve messed with. Being part of the team feels great. Keep up the good work guys :slight_smile:

1 Like