Co-locate the two cockpit-pod projects into a single repository:
- Console/ : TeslaConsole, the net48 WinForms operator console (decompiled
reconstruction) plus its differential + catalog test suite.
- Launcher/ : TeslaLauncher, the net6 pod-side Service + Agent rewrite.
Adds a combined TeslaSuite.sln, root README documenting the shared wire
contract (and its current duplication, the main follow-up), and a root
.gitignore. Histories were not preserved per request; this is a fresh start
from the current working state of both projects.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
258 lines
5.5 KiB
C#
258 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
|
|
namespace TeslaConsole;
|
|
|
|
public class PodActionDialog : Form
|
|
{
|
|
private delegate void ActionCompleted();
|
|
|
|
private class ActionItem
|
|
{
|
|
private readonly PodInfo mPodInfo;
|
|
|
|
private readonly Action<PodInfo> mAction;
|
|
|
|
private readonly ActionCompleted mActionCompleted;
|
|
|
|
private readonly ListViewItem rListViewItem;
|
|
|
|
private bool mDone;
|
|
|
|
private bool mCanceled;
|
|
|
|
private bool mError;
|
|
|
|
public ListViewItem ListViewItem => rListViewItem;
|
|
|
|
public bool Done
|
|
{
|
|
get
|
|
{
|
|
return mDone;
|
|
}
|
|
set
|
|
{
|
|
if (!mDone && value)
|
|
{
|
|
mDone = true;
|
|
mActionCompleted();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Canceled
|
|
{
|
|
get
|
|
{
|
|
return mCanceled;
|
|
}
|
|
set
|
|
{
|
|
if (!mDone && !mCanceled && value)
|
|
{
|
|
mCanceled = true;
|
|
Done = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Error => mError;
|
|
|
|
public ActionItem(PodInfo podInfo, Action<PodInfo> action, ActionCompleted complete)
|
|
{
|
|
mPodInfo = podInfo;
|
|
mAction = action;
|
|
mActionCompleted = complete;
|
|
rListViewItem = new ListViewItem(new string[2]
|
|
{
|
|
mPodInfo.Pod.Name,
|
|
"Connecting..."
|
|
});
|
|
}
|
|
|
|
public void Begin()
|
|
{
|
|
mPodInfo.EnqueueConnectAction(DoAction);
|
|
}
|
|
|
|
private void DoAction(PodInfo pod)
|
|
{
|
|
if (!mCanceled)
|
|
{
|
|
try
|
|
{
|
|
mAction(pod);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
rListViewItem.SubItems[1].Text = ex.Message;
|
|
mError = true;
|
|
}
|
|
if (!mError)
|
|
{
|
|
rListViewItem.SubItems[1].Text = "Complete";
|
|
}
|
|
Done = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private IContainer components;
|
|
|
|
private Button mCancel;
|
|
|
|
private ListView mActionList;
|
|
|
|
private ColumnHeader mPodColumn;
|
|
|
|
private ColumnHeader mStatusColumn;
|
|
|
|
private readonly List<ActionItem> mActionItems = new List<ActionItem>();
|
|
|
|
private bool mDone;
|
|
|
|
private bool mError;
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing && components != null)
|
|
{
|
|
components.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
this.mCancel = new System.Windows.Forms.Button();
|
|
this.mActionList = new System.Windows.Forms.ListView();
|
|
this.mPodColumn = new System.Windows.Forms.ColumnHeader();
|
|
this.mStatusColumn = new System.Windows.Forms.ColumnHeader();
|
|
base.SuspendLayout();
|
|
this.mCancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
|
|
this.mCancel.Location = new System.Drawing.Point(377, 247);
|
|
this.mCancel.Name = "mCancel";
|
|
this.mCancel.Size = new System.Drawing.Size(75, 23);
|
|
this.mCancel.TabIndex = 0;
|
|
this.mCancel.Text = "Cancel";
|
|
this.mCancel.UseVisualStyleBackColor = true;
|
|
this.mCancel.Click += new System.EventHandler(mCancel_Click);
|
|
this.mActionList.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right;
|
|
this.mActionList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[2] { this.mPodColumn, this.mStatusColumn });
|
|
this.mActionList.GridLines = true;
|
|
this.mActionList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
|
|
this.mActionList.Location = new System.Drawing.Point(12, 12);
|
|
this.mActionList.MultiSelect = false;
|
|
this.mActionList.Name = "mActionList";
|
|
this.mActionList.Size = new System.Drawing.Size(440, 229);
|
|
this.mActionList.TabIndex = 1;
|
|
this.mActionList.UseCompatibleStateImageBehavior = false;
|
|
this.mActionList.View = System.Windows.Forms.View.Details;
|
|
this.mPodColumn.Text = "Pod";
|
|
this.mPodColumn.Width = 300;
|
|
this.mStatusColumn.Text = "Status";
|
|
this.mStatusColumn.Width = 120;
|
|
base.AutoScaleDimensions = new System.Drawing.SizeF(6f, 13f);
|
|
base.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
|
base.ClientSize = new System.Drawing.Size(464, 282);
|
|
base.Controls.Add(this.mActionList);
|
|
base.Controls.Add(this.mCancel);
|
|
base.Name = "PodActionDialog";
|
|
this.Text = "Performing Action...";
|
|
base.ResumeLayout(false);
|
|
}
|
|
|
|
public static void Show(ICollection<PodInfo> pods, Action<PodInfo> action, string title)
|
|
{
|
|
if (pods.Count > 0)
|
|
{
|
|
PodActionDialog podActionDialog = new PodActionDialog(pods, action, title);
|
|
if (!podActionDialog.IsDisposed)
|
|
{
|
|
podActionDialog.Show();
|
|
}
|
|
}
|
|
}
|
|
|
|
private PodActionDialog()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
private PodActionDialog(IEnumerable<PodInfo> pods, Action<PodInfo> action, string title)
|
|
: this()
|
|
{
|
|
Text = title;
|
|
foreach (PodInfo pod in pods)
|
|
{
|
|
ActionItem actionItem = new ActionItem(pod, action, ItemDone);
|
|
mActionItems.Add(actionItem);
|
|
mActionList.Items.Add(actionItem.ListViewItem);
|
|
}
|
|
foreach (ActionItem mActionItem in mActionItems)
|
|
{
|
|
mActionItem.Begin();
|
|
}
|
|
if (mActionItems.Count < 1)
|
|
{
|
|
ItemDone();
|
|
}
|
|
}
|
|
|
|
private void ItemDone()
|
|
{
|
|
if (mDone)
|
|
{
|
|
return;
|
|
}
|
|
foreach (ActionItem mActionItem in mActionItems)
|
|
{
|
|
if (!mActionItem.Done)
|
|
{
|
|
return;
|
|
}
|
|
if (mActionItem.Error)
|
|
{
|
|
mError = true;
|
|
}
|
|
}
|
|
mDone = true;
|
|
if (!mError)
|
|
{
|
|
Close();
|
|
}
|
|
else
|
|
{
|
|
mCancel.Text = "OK";
|
|
}
|
|
}
|
|
|
|
private void mCancel_Click(object sender, EventArgs e)
|
|
{
|
|
Close();
|
|
}
|
|
|
|
protected override void OnClosing(CancelEventArgs e)
|
|
{
|
|
if (!mDone)
|
|
{
|
|
if (MessageBox.Show("Are you sure you wish to cancel this action?", "Cancel?", MessageBoxButtons.YesNoCancel) == DialogResult.Yes)
|
|
{
|
|
foreach (ActionItem mActionItem in mActionItems)
|
|
{
|
|
mActionItem.Canceled = true;
|
|
}
|
|
}
|
|
e.Cancel = true;
|
|
}
|
|
else
|
|
{
|
|
base.OnClosing(e);
|
|
}
|
|
}
|
|
}
|