Offline diagram generation

Hi there,

I have been experimenting with the latest Syside releases, currently Syside Modeler 0.8.7 and CLI 0.8.6.

I am trying to understand how to generate recognizable classic diagrams from SysML v2 views, especially use case, state, and sequence diagrams. I have read the documentation on SysML v2 Views and also the release post Biggest Syside Release: Views support, more diagrams and new automation features. Based on those materials, I expected that these kinds of diagrams should at least be possible in principle.

To make this concrete, I created a very small academic example: a lamp that can be turned on and off. The repository is here:

I’ve split the model into model.sysml and view.sysml:

model.sysml
package ExampleModel {
    doc
    /* Minimal SysML v2 reference model for checking current tool support
     * for structure, use cases, sequence-style interaction, and state behavior.
     *
     * The model is intentionally small and stable.
     * It is designed for parser, formatter, and view-rendering experiments rather than domain depth.
     */

    part def User;
    part def Button;
    part def Lamp;

    part def LampSystem {
        doc
        /* Structural context for the example.
         * The system is decomposed into one user, one button, and one lamp.
         */

        part user : User;
        part button : Button;
        part lamp : Lamp;
    }

    part lampSystem : LampSystem;

    use case 'operate lamp' {
        doc
        /* Top-level use case for the example.
         * This use case is intentionally simple.
         * Actor placement and child use case rendering can therefore be compared across tools.
         */

        subject lamp : Lamp;
        actor user : User;

        use case 'turn lamp on';
        use case 'turn lamp off';
    }

    use case def TurnLampOnSequence {
        doc
        /* Minimal interaction-style example.
         * The user presses the button.
         * The lamp receives the command.
         * The user then observes that the lamp is on.
         *
         * This element is included to test whether a tool only parses event occurrences and messages.
         * It also tests whether a tool can render them as a recognizable sequence-style view.
         */

        subject lamp : Lamp {
            event occurrence commandReceived;
            then
            event occurrence lampTurnedOn;
        }

        actor user : User {
            event occurrence pressesButton;
            then
            event occurrence observesLampOn;
        }

        message from user.pressesButton to lamp.commandReceived;
        then message from lamp.lampTurnedOn to user.observesLampOn;
    }

    action def TurnOn;
    action def TurnOff;

    state def LampStates {
        doc
        /* Minimal state machine definition for the example.
         * The lamp begins in Off.
         * It transitions to On on TurnOn.
         * It transitions back to Off on TurnOff.
         */
    }

    state lampStates : LampStates {
        entry;
        then off;

        state off;
        transition off accept TurnOn then on;

        state on;
        transition on accept TurnOff then off;
    }
}
views.sysml
package ExampleView {
    doc
    /* Views for the minimal lamp example.
     * These are intended to produce the most diagram-like renderings currently
     * available from the tool, rather than generic exposed-element trees.
     */

    private import Views::asTreeDiagram;

    view lampSystemContextDiagram {
        doc
        /* Expected result:
         * A compact structural context diagram rooted at lampSystem, showing
         * user, button, and lamp as contained parts.
         */
        expose ExampleModel::lampSystem;
        attribute fileName = "lamp-system-context";
        attribute fileType = "PNG";
    }

    view lampUseCaseDiagram {
        doc
        /* Expected result:
         * A top-level use case diagram centered on 'operate lamp', with User
         * shown as actor and the child use cases visible.
         */
        expose ExampleModel::'operate lamp';
        render asTreeDiagram;
        attribute fileName = "lamp-use-case";
        attribute fileType = "PNG";
    }

    view lampTurnOnSequenceDiagram {
        doc
        /* Expected result:
         * A sequence-style interaction showing the actor User, the subject Lamp,
         * and the two messages in temporal order.
         */
        expose ExampleModel::TurnLampOnSequence;
        attribute fileName = "lamp-turn-on-sequence";
        attribute fileType = "PNG";
    }

    view lampStateTransitionDiagram {
        doc
        /* Expected result:
         * A state transition diagram showing only the states off and on and the
         * transitions triggered by TurnOn and TurnOff.
         */
        expose ExampleModel::lampStates;

        filter (
            as SysML::StateUsage hastype SysML::StateUsage or
            as SysML::TransitionUsage hastype SysML::TransitionUsage
        );

        attribute fileName = "lamp-state-transition";
        attribute fileType = "PNG";
    }
}

For automatization, I’ve ceated a Taskfile.yml:

Taskfile.yml
version: "3"

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list
    silent: true

  check:
    desc: Validate the lamp example files
    cmds:
      - syside check model/model.sysml model/view.sysml

  format:
    desc: Format the lamp example files
    cmds:
      - syside format model/model.sysml model/view.sysml

  clean:
    desc: Remove generated view output
    cmds:
      - powershell -NoProfile -Command "if (Test-Path output/views) { Remove-Item output/views -Recurse -Force }"

  view-context:
    desc: Render the lamp context diagram
    cmds:
      - syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampSystemContextDiagram --output-dir output/views

  view-usecase:
    desc: Render the lamp use case diagram
    cmds:
      - syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampUseCaseDiagram --output-dir output/views

  view-state:
    desc: Render the lamp state transition diagram
    cmds:
      - syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampStateTransitionDiagram --output-dir output/views

  view-sequence:
    desc: Render the lamp sequence diagram
    cmds:
      - syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampTurnOnSequenceDiagram --output-dir output/views

  views:
    desc: Render all lamp diagrams
    cmds:
      - task: view-context
      - task: view-usecase
      - task: view-state
      - task: view-sequence

To create the different view, I use:

  1. To render the lamp context diagram using
    syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampSystemContextDiagram --output-dir output/views
lamp-system-context.png

  1. To render the lamp use case diagram
    syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampUseCaseDiagram --output-dir output/views
lamp-use-case.png

  1. To render the lamp state transition diagram
    syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampStateTransitionDiagram --output-dir output/views
lamp-state-transition

  1. To render the lamp sequence diagram
    syside viz view model/model.sysml model/view.sysml --qualified-name ExampleView::lampTurnOnSequenceDiagram --output-dir output/views
lamp-turn-on-sequence

My main issue is that the outputs look more like generic exposed model views than the classic diagrams I was trying to produce.

In particular:

  • the use case diagram exposes internal structure that I would not expect in a communication-oriented use case diagram
  • the state diagram still shows inherited/internal action structure that I was trying to filter out
  • the sequence example parses, but the rendering does not resemble a usable sequence diagram

So my questions are:

  1. Am I modeling the views in the wrong way?
  2. Are there recommended patterns for getting more recognizable use case, sequence, and state diagrams out of Syside today?
  3. Should I be using specific standard view definitions such as SequenceView or StateTransitionView instead of plain view with expose?
  4. Do you have examples of how you are generating more polished diagrams from Syside in practice?

I would appreciate any guidance, example repositories, or pointers to working patterns.

Thanks in advance.

Hi Manuel,

The diagram rendering functionality in Syside is powered by tools from Tom Sawyer Software. There are, unfortunately, limits on how much the styling can be altered.

Reading the standard (7.26) on views will give you an idea of how you can control the appearance by changing how you represent the view in textual notation (e.g. filter may be useful to you).

As for your other questions, my colleagues may be able to give you feedback regarding how to utilize StandardViewDefinitions. It is the case that, for example, SequenceView is the default compartment for an occurrence.

Best,

Adam Layne, PhD

Hi Adam,

I’ll check the standard. Thanks for the pointer. I understand the reliance on Tom Sawyer for diagram generation and that their are limitations.

Still I hope we can create an example that might be useful for others.

Best wishes,
Manuel

Hi Manuel,

I’ll try to answer some of your questions one-by-one:


Am I modeling the views in the wrong way?

I would not say you are modeling them the “wrong” way, but there are some issues with how you define what you would like to be shown in the view.

To better communicate, let’s consider the following simplistic model:

package PackageA {
    part def a {
        part b;
        part c {
            part d;
            part e;
        }
    }
}

package View {
    view exposeExample {
        expose PackageA::a;
    }
}

Now, let’s talk about expose and different options you have:

  • If expose is PackageA::a; , then exposed elements would be the element itself:
    • PackageA::a
  • If expose is PackageA::a::*; , then exposed elements would be only the direct children:
    • PackageA::a::b
    • PackageA::a::c
  • If expose is PackageA::a::**; , then exposed elements would be the element itself, direct children, and children-of-children recursively:
    • PackageA::a
    • PackageA::a::b
    • PackageA::a::c
    • PackageA::a::c::d
    • PackageA::a::c::e
  • If expose is PackageA::a::*::**; , then exposed elements would be direct children, and children-of-children recursively:
    • PackageA::a::b
    • PackageA::a::c
    • PackageA::a::c::d
    • PackageA::a::c::e

Now, the reason why in Tom Sawyer you see more elements than what “pure” expose would give you is because our current implementation has a depth attribute that is set to -1 by default. This means, that the visualizer takes the exposed element, and traverses its sub-tree up to the value set by depth (-1 means infinite) and visualizes all the traversed elements. This is one of the reasons why in the use case diagrams you see more details than you’d like.


Are there recommended patterns for getting more recognizable use case, sequence, and state diagrams out of Syside today?

Sequence views

When Tom Sawyer visualizer encounters an occurrence with parts and message flows inside, it will try to visualize it as the sequence view automatically. In your case, this would mean translating your TurnLampOnSequence from a use case to an occurrence def:

    occurrence def TurnLampOnSequence_occurrence {
        part lamp : Lamp {
            event occurrence commandReceived;
            then event occurrence lampTurnedOn;
        }
        part user : User {
            event occurrence pressesButton;
            then event occurrence observesLampOn;
        }

        message from user.pressesButton to lamp.commandReceived;
        then message from lamp.lampTurnedOn to user.observesLampOn;
    }

This would produce a diagram like so. There is currently a bug in the Tom Sawyer visualizer that makes the arrow directions to randomly not correspond with what is defined in the model. We have raised this issue with them and they are looking into it.

Use case

As described in the answer to your first question, this is an issue of depth and expose. I recommend changing the view to the following:

    view operateLampUseCaseView {
        doc
        /* Expected result:
         * A use case view centered on 'operate lamp'.
         * User is shown as an actor.
         * The child use cases 'turn lamp on' and 'turn lamp off' are visible beneath it.
         *
         * Tree rendering is used here to make the view deterministic.
         */
        expose ExampleModel::'operate lamp'; // Exposes the use case itself
        expose ExampleModel::'operate lamp'::*; // Exposes direct children
        render asTreeDiagram;

        // Hide the documentation node as it is already in the use case
        // as a compartment
        filter not @SysML::Documentation;

        // Shows the relationships between the use case and its children.
        // Set to 0 if you do not want to see the relations.
        attribute depth = 1; 
    }

This produces the following diagram, which seems to be what you are looking for according to the doc.

State machine

The solution to this is a combination of the above two answers:

  1. Tom Sawyer visualizer auto-detects that the diagram is a state machine view when the element being visualized is a state def (which you already discovered).
  2. The fix of removing superfluous data is, again, fixing the exposes and setting depth:
    view lampStatesView {
        doc
        /* Expected result:
         * A state-oriented rendering with the states off and on.
         * It shows the transitions triggered by TurnOn and TurnOff.
         *
         * This view is used to check whether tools provide a recognizable state-diagram rendering.
         * It also checks whether they only provide a generic model view.
         */
        expose ExampleModel::lampStates;
        expose ExampleModel::lampStates::*;

        attribute depth = 1;
    }

The above <<action>> should be the “entry” state circle. It is not rendering as such due to a bug at the moment. This also loses the transition names at the moment. We are working to address this.


Should I be using specific standard view definitions such as SequenceView or StateTransitionView instead of plain view with expose?

At the moment, Tom Sawyer visualizer does not have a separate “sequence view” or “state transition view”, only the “tree” and “nested” views (which are very generic views). As seen above, Tom Sawyer instead looks into what is being visualized and if it finds an occurrence def or state def in the model, and visualizes it in that specific way.

Thus, at the moment, there is no need to use those view definitions as they will not have any effect.


Do you have examples of how you are generating more polished diagrams from Syside in practice?

This is essentially what we do: we play around with expose and depth to see what looks the best. We are also working on improvements some of which are coming in 0.9.0 and some of them were teased in another forum post: Customizing Diagrams - #6 by Simonas

2 Likes

Hi @Simonas,

Thank you for the detailed response — your guidance on occurrence def, expose/depth patterns, and the sequence diagram arrow direction bug has been incredibly helpful.

I’ve been building a SysML v2 domain library using Syside and ran into the same arrow direction issue while trying to recreate the sequence diagram from the Syside v0.8.5 blog post. I ran a series of systematic experiments to understand the bug’s scope and wanted to share the findings in case they’re useful for the Tom Sawyer visualization investigation.

Summary:

All arrows render left-to-right regardless of from/to direction. In our experiments, this wasn’t random — it was consistently unidirectional. Every message arrow pointed left-to-right, even when the model clearly specified a right-to-left flow (e.g., a response from server back to client). The Syside Automator API confirms the source/target are stored correctly in the model — only the visualization is wrong.

Minimal reproduction cases

We tested on Syside Modeler 0.8.7 (macOS, darwin-arm64).

Test 1 — Two-party request/response (bare occurrence):

part def Server;
part def Client;

occurrence requestResponse {
    part client : Client {
        event occurrence sendRequest;
        then event occurrence receiveResponse;
    }
    part server : Server {
        event occurrence receiveRequest;
        then event occurrence sendResponse;
    }
    message request from client.sendRequest to server.receiveRequest;
    then message response from server.sendResponse to client.receiveResponse;
}

Expected: request arrow left→right (client→server), response arrow right→left (server→client).
Actual: Both arrows render left→right.

Test 2 — Same protocol with occurrence def:

part def Server;
part def Client;

occurrence def RequestResponseDef {
    part client : Client {
        event occurrence sendRequest;
        then event occurrence receiveResponse;
    }
    part server : Server {
        event occurrence receiveRequest;
        then event occurrence sendResponse;
    }
    message request from client.sendRequest to server.receiveRequest;
    then message response from server.sendResponse to client.receiveResponse;
}

Result: Same behavior — both arrows left→right. Additionally, the last-declared lifeline shows :> suboccurrences instead of :> parts (cosmetic issue).

Test 3 — Three-party protocol (4 messages, then message chain):

part def ClientNode;
part def ServerNode;
part def DatabaseNode;

occurrence def ThreePartyProtocol {
    part client : ClientNode {
        event occurrence sendQuery;
        then event occurrence receiveResult;
    }
    part server : ServerNode {
        event occurrence receiveQuery;
        then event occurrence forwardToDb;
        then event occurrence receiveDbResult;
        then event occurrence sendResult;
    }
    part database : DatabaseNode {
        event occurrence receiveDbQuery;
        then event occurrence returnDbResult;
    }
    message query from client.sendQuery to server.receiveQuery;
    then message dbQuery from server.forwardToDb to database.receiveDbQuery;
    then message dbResult from database.returnDbResult to server.receiveDbResult;
    then message result from server.sendResult to client.receiveResult;
}

Expected: query (client→server), dbQuery (server→database), dbResult (database→server ←), result (server→client ←).
Actual: All four arrows render left→right. The then message chain correctly orders them top-to-bottom (temporal axis works), but the horizontal direction is wrong for dbResult and result.

Verification via Automator API

I confirmed the model stores correct directionality using the Automator SDK. Messages are represented as FlowUsage with child EventOccurrenceUsage elements pointing to the correct source and target events. The then message construct creates SuccessionAsUsage elements with earlierOccurrence/laterOccurrence references.

Additional observations

  1. then message temporal ordering works. The vertical top-to-bottom sequence correctly reflects the then chain. This is a great feature — only the horizontal arrow direction is broken.

  2. Lifeline ordering is not deterministic. Parts declared in the same order (client first, server second) get placed in different left-right positions across renders and between occurrence def vs bare occurrence. This may be related to the arrow direction issue.

  3. filter not @SysML::Documentation; in view definitions didn’t suppress the doc compartment in our tests (v0.8.7). Is this expected, or should it be working?

  4. depth = 1 is essential — your recommendation to use attribute depth = 1; in view definitions completely solved the stdlib noise problem (:> subactions, ^stateTransitions compartments). Thank you for that tip — it should perhaps be in the documentation more prominently.

Questions

  1. You described the arrow direction as “random” — in our experiments, it was consistently left-to-right for all arrows. Have you observed cases where arrows go right-to-left, or is the bug specifically that all arrows default to one direction?

  2. Is there an ETA or version target for the Tom Sawyer fix? We’re preparing figures for a journal submission and wanted to know whether to wait or manually correct arrows in exported SVGs.

  3. For the lifeline ordering — is there a way to control which part appears on the left vs. right? Declaration order doesn’t seem to determine it.

Thank you again for the excellent support. Happy to provide any additional test cases if they’d help.

1 Like

Thank you so much @Simonas and @ssd for jumping in.

I’ll try to revise my example tomorrow and post an update here as well as on github.

Thanks for the heads up on what is supposed to work and what is on the horizon.

Hi @ssd ,

The filter is not supposed to suppress the doc compartment, only its node. Tom Sawyer visualizer currently has no options to show/hide compartments.

Diagram without the filter:

Diagram with the filter:

We have encountered cases in the interactive visualizer (the one in Modeler, not in Modeler CLI) where the arrows would randomly flip direction when interacting with the diagram. You are correct that initially all arrows go left-to-right and that’s what’s seen on the generated static diagrams from the CLI.

Not at the moment. If you have a specific deadline by which this should be fixed, please let us know and we will communicate this with Tom Sawyer Software to help with prioritization.

Not at the moment. The data we pass to Tom Sawyer is ordered the same as in the textual notation, but their visualizer parses the data in a non-deterministic order. This is an issue we have raised with them, but have not yet heard of any ETAs.

1 Like

Thanks again for the clarification in this thread.

Based on that guidance, I updated the GitHub example and reduced it to a much smaller second-iteration baseline. That cleanup helped a lot.

The current version now produces results much closer to the blog-post style we were originally trying to reproduce.

GitHub update: GitHub - mkudruss/sysmlv2-lamp-onoff-example: This repository is a compact SysML v2 reference project for checking what current tooling can parse, format, and render from a small but representative model. · GitHub

Results

System Context


State Transition

Use Case

Sequence Diagrams


So overall: your clarification helped, and the updated repo now reflects that understanding much better.

Thanks again.

2 Likes