184 lines
6.3 KiB
C#
184 lines
6.3 KiB
C#
using System;
|
||||
|
|
using System.IO;
|
|||
|
|
using CartActivator;
|
|||
|
|
using CycleGUI;
|
|||
|
|
using CycleGUI.API;
|
|||
|
|
using Medulla.Types;
|
|||
|
|
|
|||
|
|
namespace MultiWheelM;
|
|||
|
|
|
|||
|
|
public partial class CartDefinition
|
|||
|
|
{
|
|||
|
|
private bool _fleetRemoteActive;
|
|||
|
|
private DateTime _fleetDiagLastStick = DateTime.MinValue;
|
|||
|
|
|
|||
|
|
// 写盘诊断日志(Medulla 侧)。文件落在 Medulla 工作目录,便于复现后离线分析。
|
|||
|
|
// 每次进程启动清空一次(_fleetDiagInit),同一次运行内追加,避免跨多次运行累积。
|
|||
|
|
private static bool _fleetDiagInit;
|
|||
|
|
private void FleetDiag(string msg)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
const string path = "fleet_diag_medulla.log";
|
|||
|
|
var line = $"{DateTime.Now:HH:mm:ss.fff} car{CarNum} {msg}{Environment.NewLine}";
|
|||
|
|
if (!_fleetDiagInit)
|
|||
|
|
{
|
|||
|
|
_fleetDiagInit = true;
|
|||
|
|
File.WriteAllText(path,
|
|||
|
|
$"=== session start {DateTime.Now:yyyy-MM-dd HH:mm:ss} ==={Environment.NewLine}" + line);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
File.AppendAllText(path, line);
|
|||
|
|
}
|
|||
|
|
catch
|
|||
|
|
{
|
|||
|
|
// 诊断日志失败不影响主流程
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ZeroFleetManualFields()
|
|||
|
|
{
|
|||
|
|
MultiVehicleManualEnabled = false;
|
|||
|
|
MultiVehicleManualMode = 0;
|
|||
|
|
MultiVehicleManualVx = 0;
|
|||
|
|
MultiVehicleManualVy = 0;
|
|||
|
|
MultiVehicleManualVth = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
[IOObjectUtility]
|
|||
|
|
public void FleetRemote()
|
|||
|
|
{
|
|||
|
|
if (_fleetRemoteActive)
|
|||
|
|
{
|
|||
|
|
GUI.GetPanelInstancingObject(this)?.BringToFront();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_fleetRemoteActive = true;
|
|||
|
|
var fleetOn = false;
|
|||
|
|
// 速度比例 0~1,作用于摇杆输出的线速度与转向角。
|
|||
|
|
var speedRatio = 1f;
|
|||
|
|
UseGesture? manip = null;
|
|||
|
|
|
|||
|
|
void CleanupFleetRemote()
|
|||
|
|
{
|
|||
|
|
manip?.End();
|
|||
|
|
manip = null;
|
|||
|
|
fleetOn = false;
|
|||
|
|
ZeroFleetManualFields();
|
|||
|
|
_fleetRemoteActive = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
manip = new UseGesture();
|
|||
|
|
|
|||
|
|
manip.AddWidget(new UseGesture.ToggleWidget
|
|||
|
|
{
|
|||
|
|
name = "fleet_enable",
|
|||
|
|
text = "车队联动",
|
|||
|
|
position = "12.5%+10px, 75%+10px",
|
|||
|
|
size = "25%-10px, 12.5%-10px",
|
|||
|
|
OnValue = b =>
|
|||
|
|
{
|
|||
|
|
fleetOn = b;
|
|||
|
|
MultiVehicleManualEnabled = b;
|
|||
|
|
MultiVehicleManualMode = 0;
|
|||
|
|
if (!b)
|
|||
|
|
ZeroFleetManualFields();
|
|||
|
|
FleetDiag($"TOGGLE fleetOn={b} -> ManualEnabled={MultiVehicleManualEnabled}");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
manip.AddWidget(new UseGesture.ThrottleWidget
|
|||
|
|
{
|
|||
|
|
name = "fleet_speed_ratio",
|
|||
|
|
text = "速度比例",
|
|||
|
|
position = "50%+10px, 75%+10px",
|
|||
|
|
size = "50%-10px, 12.5%-10px",
|
|||
|
|
bounceBack = false,
|
|||
|
|
OnValue = (val, _) => speedRatio = Math.Clamp(val, 0, 1)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
manip.AddWidget(new UseGesture.ButtonWidget
|
|||
|
|
{
|
|||
|
|
name = "fleet_stop",
|
|||
|
|
text = "急停/归零",
|
|||
|
|
position = "25%+10px, 87.5%+10px",
|
|||
|
|
size = "25%-10px, 12.5%-10px",
|
|||
|
|
OnPressed = _ =>
|
|||
|
|
{
|
|||
|
|
MultiVehicleManualVx = 0;
|
|||
|
|
MultiVehicleManualVy = 0;
|
|||
|
|
MultiVehicleManualVth = 0;
|
|||
|
|
FleetDiag("BUTTON stop/zero");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
manip.AddWidget(new UseGesture.StickWidget
|
|||
|
|
{
|
|||
|
|
name = "fleet_stick",
|
|||
|
|
text = "速度摇杆",
|
|||
|
|
position = "37.5%+10px, 25%+10px",
|
|||
|
|
size = "37.5%-10px, 37.5%-10px",
|
|||
|
|
bounceBack = true,
|
|||
|
|
keyboard = "Up,Down,Left,Right",
|
|||
|
|
joystick = "Axis0,Axis1",
|
|||
|
|
OnValue = (pos, manipulating) =>
|
|||
|
|
{
|
|||
|
|
if (!fleetOn)
|
|||
|
|
{
|
|||
|
|
MultiVehicleManualVx = 0;
|
|||
|
|
MultiVehicleManualVy = 0;
|
|||
|
|
MultiVehicleManualVth = 0;
|
|||
|
|
if ((DateTime.Now - _fleetDiagLastStick).TotalMilliseconds >= 200)
|
|||
|
|
{
|
|||
|
|
_fleetDiagLastStick = DateTime.Now;
|
|||
|
|
FleetDiag($"STICK(ignored,fleetOff) pos=({pos.X:0.00},{pos.Y:0.00}) manip={manipulating}");
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// pos.Y(前后) → 车队线速度(m/s);pos.X(左右) → 车队转向角(deg)。松手 bounceBack 自动回零。
|
|||
|
|
var ratio = Math.Clamp(speedRatio, 0, 1);
|
|||
|
|
MultiVehicleManualVx = Math.Clamp(pos.Y, -1, 1) * MaxManualSpeed * ratio;
|
|||
|
|
MultiVehicleManualVy = 0;
|
|||
|
|
MultiVehicleManualVth = Math.Clamp(pos.X, -1, 1) * MaxManualAngularSpeed * ratio;
|
|||
|
|
|
|||
|
|
if ((DateTime.Now - _fleetDiagLastStick).TotalMilliseconds >= 200)
|
|||
|
|
{
|
|||
|
|
_fleetDiagLastStick = DateTime.Now;
|
|||
|
|
FleetDiag($"STICK pos=({pos.X:0.00},{pos.Y:0.00}) manip={manipulating} ratio={ratio:0.00} " +
|
|||
|
|
$"maxV={MaxManualSpeed:0.00} maxW={MaxManualAngularSpeed:0.0} " +
|
|||
|
|
$"-> Vx={MultiVehicleManualVx:0.000} Vth={MultiVehicleManualVth:0.0} en={MultiVehicleManualEnabled}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
manip.ChangeState(new SetAppearance { drawGuizmo = false });
|
|||
|
|
manip.Start();
|
|||
|
|
|
|||
|
|
GUI.PromptOrBringToFront(pb =>
|
|||
|
|
{
|
|||
|
|
if (pb.Closing())
|
|||
|
|
{
|
|||
|
|
CleanupFleetRemote();
|
|||
|
|
pb.Panel.Exit();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pb.Panel.TopMost(true)
|
|||
|
|
.SetDefaultDocking(Panel.Docking.None)
|
|||
|
|
.ShowTitle("车队联动遥控")
|
|||
|
|
.InitSize(320, 160)
|
|||
|
|
.InitPos(false, -32, 32, 1, 0, 1, 0);
|
|||
|
|
|
|||
|
|
pb.SeparatorText("状态");
|
|||
|
|
pb.Label($"车队联动: {MultiVehicleManualEnabled}");
|
|||
|
|
pb.Label($"速度比例: {speedRatio:0.00}");
|
|||
|
|
pb.Label($"Vx={MultiVehicleManualVx:0.000} m/s, Vth={MultiVehicleManualVth:0.0} deg/s");
|
|||
|
|
pb.Label($"优先级: {CartActivator.CartDefinition.currentPriority} ({CartActivator.CartDefinition.currentPriorityDesc})");
|
|||
|
|
pb.Label("请勿同时打开「手动控制」面板");
|
|||
|
|
pb.Panel.Repaint();
|
|||
|
|
}, instancingObject: this);
|
|||
|
|
}
|
|||
|
|
}
|