Table of Contents

Player Tool Bar

Layout

  • Player Tool Bar
    • Status Text Field
    • Start Button
    • Pause Button
    • Run-One-Line Button
    • Run-One-Step Button
    • Stop Button
    • Reset Button

Behavior of Player Tool Bar

See the example code to:

  • complete the behavior of the buttons and Status Text Field.
  • The rapidly used buttons should has hotkey. At least the following buttons:
    • Run One Line Button
    • Run One Step Button
    • Start/Continue
    • Pause
  • Add and remove the event to the PacePlayer on Player Tool Bar initialization and finalization. The events update the Status Text Field.
  • Alter the background color of the Status Text Field if the status changed.
    • Warning style color
      • Running
    • Secondary style color
      • Paused
      • No Project
    • Success style color
      • Finished
      • Ready

The action of Reset Button should be async for user experience.

Tip

Use icon instead of text to the tool bar button. Run One Line Button and Run One Step Button use the same icon, use the different color to resolve them.

  • Run One Line Button > default color with green seasoned
  • Run One Step Button > default color with blue seasoned

The other button use the default color is enough.

Example Code

@using Hi.Common.PathUtils;
@using Hi.HiNcKits;
@using Hi.MachiningProcs
@using Hi.MillingProcs;
@using Hi.Numerical.FilePlayers;
@inject HiNcHost hiNcHost

@{
    MachiningProject machiningProject = hiNcHost.MachiningProject;
    bool disabledByMachiningProject = machiningProject == null;
}

<div class="btn-group" role="group">
    <div class=" btn-group"
         data-bs-toggle="collapse" data-bs-target="#player-@Tid">
        <button class="btn btn-outline-info text-nowrap "
                disabled="@disabledByMachiningProject"
                data-bs-toggle="button">
            <span class="me-1">@Loc["Player"]</span>
            <div class="d-inline-block" style="width: 4rem">
                @{
                    if (machiningProject == null) { }
                    else if (machiningProject.PacePlayer.IsRunning)
                    {
                        <span class="badge text-bg-warning">@Loc["Running"]</span>
                    }
                    else if (machiningProject.PacePlayer.IsLocked)
                    {
                        <span class="badge text-bg-secondary">@Loc["Pause"]</span>
                    }
                    else if (machiningProject.PacePlayer.IsFinished)
                    {
                        <span class="badge text-bg-success">@Loc["Finish"]</span>
                    }
                    else
                    {
                        <span class="badge text-bg-primary">@Loc["Unlocked"]</span>
                    }
                }
            </div>
        </button>
    </div>

    <div id="player-@Tid" class="btn-group collapse collapse-horizontal show" role="group">

        @if (machiningProject == null) { }
        else if (!machiningProject.PacePlayer.IsLocked)
        {
            <button class="btn btn-primary text-nowrap" title="@Loc["Start"] (S)"
                    accesskey="s"
                    disabled="@(disabledByMachiningProject||machiningProject.PacePlayer.IsFinished)"
                    @onclick="StartOrContinue">
                <span class="oi oi-media-play me-1"></span>
            </button>
        }
        else
        {
            <button class="btn btn-primary text-nowrap" title="@Loc["Continue"] (S)"
                    accesskey="s"
                    @onclick="StartOrContinue"
                    disabled="@(disabledByMachiningProject||machiningProject.PacePlayer.IsFinished||machiningProject.PacePlayer.IsRunning)">
                <span class="oi oi-media-play me-1"></span>
            </button>
        }
        <button class="btn btn-primary text-nowrap" title="@Loc["Pause"] (P)"
                accesskey="p"
                @onclick="Pause"
                disabled="@(disabledByMachiningProject||!machiningProject.PacePlayer.IsRunning)">
            <span class="oi oi-media-pause me-1"></span>
        </button>
        <button class="btn btn-primary text-nowrap" title="@Loc["Run One Line"] (L)"
                accesskey="l"
                @onclick="RunToLineEnd"
                disabled="@(disabledByMachiningProject||machiningProject.PacePlayer.IsFinished)">
            <span class="oi oi-media-step-forward me-1"></span>
        </button>

        <button class="btn btn-primary text-nowrap" title="@Loc["Run One Step"] (K)"
                accesskey="k"
                @onclick="RunToNextPace"
                disabled="@(disabledByMachiningProject||machiningProject.PacePlayer.IsFinished)">
            <CommonRcl.Shared.CombinedIcon>
                <IconA>
                    <span class="oi oi-media-step-forward me-1"></span>
                </IconA>
                <IconB>
                    <span class="badge rounded-pill bg-primary-subtle text-primary-emphasis">
                        step
                    </span>
                </IconB>
            </CommonRcl.Shared.CombinedIcon>
        </button>

        <button class="btn btn-primary text-nowrap" title="@Loc["Break"]"
                @onclick="@Break"
                disabled="@(disabledByMachiningProject||!(machiningProject.PacePlayer.IsLocked||machiningProject.PacePlayer.IsFinished))">
            <span class="oi oi-media-stop me-1"></span>
        </button>

        <button class="btn btn-primary text-nowrap" title="@Loc["Reset"]"
                disabled="@disabledByMachiningProject"
                @onclick="Reset">
            <span class="bi bi-backspace"></span>
        </button>
    </div>
</div>

using Hi.Common;
using Hi.MachiningProcs;
using Hi.Parallels;
using Microsoft.AspNetCore.Components;

namespace HiNcRcl.Areas.Player
{
    public partial class PlayerButtonGroup : IAsyncDisposable
    {
        [Parameter]
        public string Tid { set; get; } = System.Guid.NewGuid().ToString();
        StringLocalizer Loc { get; } = new StringLocalizer(typeof(PlayerDiv));
        SemaphoreSlim DisposeSemaphore { get; } = new SemaphoreSlim(1);
        MachiningProject MachiningProject => hiNcHost.MachiningProject;
        bool disposedValue = false;
        /// <inheritdoc/>
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            base.OnAfterRender(firstRender);
            if (firstRender)
            {
                using var _ = await DisposeSemaphore.EmbraceAsync();
                if (disposedValue) return;
                var machiningProject = MachiningProject;
                if (machiningProject != null)
                {
                    machiningProject.PacePlayer.IsLockedEventHandler
                        += EnumerablePlayer_IsLockedEventHandler;
                    machiningProject.PacePlayer.IsRunningChangedEvent
                        += EnumerablePlayer_IsLockedEventHandler;
                }
            }
        }
        /// <inheritdoc/>
        public async ValueTask DisposeAsync()
        {
            using var _ = await DisposeSemaphore.EmbraceAsync();
            var machiningProject = MachiningProject;
            if (machiningProject != null)
            {
                machiningProject.PacePlayer.IsLockedEventHandler
                    -= EnumerablePlayer_IsLockedEventHandler;
                machiningProject.PacePlayer.IsRunningChangedEvent
                    -= EnumerablePlayer_IsLockedEventHandler;
            }
            disposedValue = true;
            await ValueTask.CompletedTask;
        }

        private void EnumerablePlayer_IsLockedEventHandler(bool obj)
        {
            InvokeAsync(StateHasChanged).ConfigureAwait(false);
        }

        public void StartOrContinue()
        {
            if (!MachiningProject.PacePlayer.IsLocked)
            {
                MachiningProject.PacePlayer.Start();
            }
            else if (!MachiningProject.PacePlayer.IsRunning
            && !MachiningProject.PacePlayer.IsFinished)
            {
                MachiningProject.PacePlayer.Resume();
            }
        }
        public void Pause()
        {
            MachiningProject?.PacePlayer.Pause();
        }
        public void RunToLineEnd()
        {
            MachiningProject?.NcRunner.RunToLineEnd();
        }
        public void RunToNextPace()
        {
            MachiningProject?.PacePlayer.RunToNextPace();
        }
        public void Break()
        {
            MachiningProject?.PacePlayer.Terminate();
        }
        public async Task Reset()
        {
            await Task.Run(() => {
                MachiningProject?.PacePlayer.Reset();
            }).ShowIfCatched(this);
        }
    }
}

Single-User WPF Application Source Code Path

  • Play/PlayerToolBar

see this page for git repository.