Files
Tutorial/MultiWheel/MultiWheelM/FleetRemote.cs
T
ruifeng.zhouandCursor 235c6c70ac Add Clumsy plugins and dual-vehicle fleet sync demo.
Restructure into DiffWheel/MultiWheel folders, add DiffWheelC and MultiWheelC with MovementTests, FleetRemote multi-vehicle manual sync, deploy templates, and documentation. Update dependency paths to D:\MDCS\Release and mirror motor feedback in MotorRoutine for simulation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 15:04:55 +08:00

184 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}