-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMainWindow.xaml.cs
More file actions
362 lines (288 loc) · 11.7 KB
/
Copy pathMainWindow.xaml.cs
File metadata and controls
362 lines (288 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using FAC1_Controller_Windows.Services;
using FAC1_Controller_Windows.Controllers;
namespace FAC1_Controller_Windows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, IDisposable
{
private SerialPortManager? _serialPortManager;
private ServoController? _servoController;
private bool _disposed = false;
private AppSettings _settings;
// Status indicator colors
private readonly SolidColorBrush _connectedBrush = new(Colors.LimeGreen);
private readonly SolidColorBrush _disconnectedBrush = new(Colors.Red);
public MainWindow()
{
InitializeComponent();
// Load settings
_settings = AppSettings.Load();
InitializeControllers();
ApplySettings();
// Start auto-connection
_ = Task.Run(AttemptAutoConnection);
}
private void InitializeControllers()
{
_serialPortManager = new SerialPortManager();
_servoController = new ServoController(_serialPortManager);
// Subscribe to events
_serialPortManager.ConnectionStatusChanged += OnConnectionStatusChanged;
_serialPortManager.StatusMessageChanged += OnStatusMessageChanged;
_servoController.StatusMessageChanged += OnStatusMessageChanged;
_servoController.PanServoStatusChanged += OnPanServoStatusChanged;
_servoController.TiltServoStatusChanged += OnTiltServoStatusChanged;
}
private void ApplySettings()
{
// Apply always on top
this.Topmost = _settings.AlwaysOnTop;
if (AlwaysOnTopCheckBox != null)
{
AlwaysOnTopCheckBox.IsChecked = _settings.AlwaysOnTop;
}
// Apply axis inversions
if (_servoController != null)
{
_servoController.InvertPanAxis = _settings.InvertPanAxis;
_servoController.InvertTiltAxis = _settings.InvertTiltAxis;
}
// Update menu items
if (InvertPanMenuItem != null)
{
InvertPanMenuItem.IsChecked = _settings.InvertPanAxis;
}
if (InvertTiltMenuItem != null)
{
InvertTiltMenuItem.IsChecked = _settings.InvertTiltAxis;
}
}
private void SaveSettings()
{
_settings.Save();
}
private async Task AttemptAutoConnection()
{
if (_serialPortManager == null) return;
await Task.Delay(1000); // Brief delay on startup
UpdateStatus("Searching for USB-serial devices...");
if (await _serialPortManager.AutoConnectAsync())
{
// Initialize servos after successful connection
await _servoController?.InitializeServosAsync()!;
}
}
#region Event Handlers
private void OnConnectionStatusChanged(object? sender, bool connected)
{
Dispatcher.Invoke(() =>
{
UsbStatusIndicator.Fill = connected ? _connectedBrush : _disconnectedBrush;
UsbStatusText.Text = connected ?
$"USB: Connected ({_serialPortManager?.CurrentPortName})" :
"USB: Disconnected";
// Update servo status visibility and button states
UpdateServoStatusVisibility();
UpdateControlButtonStates();
});
}
private void OnPanServoStatusChanged(object? sender, bool connected)
{
Dispatcher.Invoke(() =>
{
UpdateServoStatusVisibility();
PanStatusIndicator.Fill = connected ? _connectedBrush : _disconnectedBrush;
PanStatusText.Text = connected ?
"Pan Servo (ID 6): Connected" :
"Pan Servo (ID 6): Disconnected";
UpdateControlButtonStates();
});
}
private void OnTiltServoStatusChanged(object? sender, bool connected)
{
Dispatcher.Invoke(() =>
{
UpdateServoStatusVisibility();
TiltStatusIndicator.Fill = connected ? _connectedBrush : _disconnectedBrush;
TiltStatusText.Text = connected ?
"Tilt Servo (ID 4): Connected" :
"Tilt Servo (ID 4): Disconnected";
UpdateControlButtonStates();
});
}
private void OnStatusMessageChanged(object? sender, string message)
{
UpdateStatus(message);
}
#endregion
#region Control Button Events
private void PanLeftButton_Click(object sender, RoutedEventArgs e)
{
_servoController?.MovePanLeft();
}
private void PanRightButton_Click(object sender, RoutedEventArgs e)
{
_servoController?.MovePanRight();
}
private void TiltUpButton_Click(object sender, RoutedEventArgs e)
{
_servoController?.MoveTiltUp();
}
private void TiltDownButton_Click(object sender, RoutedEventArgs e)
{
_servoController?.MoveTiltDown();
}
private void CenterButton_Click(object sender, RoutedEventArgs e)
{
_servoController?.CenterCamera();
}
private async void ReconnectButton_Click(object sender, RoutedEventArgs e)
{
if (_serialPortManager == null || _servoController == null) return;
UpdateStatus("Reconnecting...");
// Disconnect and reconnect
_serialPortManager.Disconnect();
await Task.Delay(500);
if (await _serialPortManager.AutoConnectAsync())
{
await _servoController.InitializeServosAsync();
}
}
#endregion
#region Settings Events
private void AlwaysOnTopCheckBox_Checked(object sender, RoutedEventArgs e)
{
this.Topmost = true;
_settings.AlwaysOnTop = true;
SaveSettings();
}
private void AlwaysOnTopCheckBox_Unchecked(object sender, RoutedEventArgs e)
{
this.Topmost = false;
_settings.AlwaysOnTop = false;
SaveSettings();
}
#endregion
#region Menu Events
private void SetCenterMenuItem_Click(object sender, RoutedEventArgs e)
{
if (_servoController == null) return;
var result = MessageBox.Show(
"This will change the default center position of the servos.\n\n" +
"If you click yes to continue, servo torque will be disabled so you can manually position the camera where you want the center to be, then click OK.\n\n" +
"Continue?",
"Set Center Position",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
// Disable torque to allow manual positioning
_servoController.DisableTorque();
// Show instructions
var positionResult = MessageBox.Show(
"Torque disabled. You can now manually move the camera.\n\n" +
"Position the camera at the desired center point, then click OK to save this position.\n\n" +
"Click Cancel to abort without saving.",
"Position Camera Manually",
MessageBoxButton.OKCancel,
MessageBoxImage.Information);
if (positionResult == MessageBoxResult.OK)
{
// Calibrate at current position (writes to EEPROM)
_servoController.SetCenterPosition();
}
// Re-enable torque
_servoController.EnableTorque();
}
}
private void InvertPanMenuItem_Click(object sender, RoutedEventArgs e)
{
if (_servoController != null)
{
_servoController.InvertPanAxis = InvertPanMenuItem.IsChecked;
_settings.InvertPanAxis = InvertPanMenuItem.IsChecked;
SaveSettings();
UpdateStatus($"Pan axis inversion: {(InvertPanMenuItem.IsChecked ? "Enabled" : "Disabled")}");
}
}
private void InvertTiltMenuItem_Click(object sender, RoutedEventArgs e)
{
if (_servoController != null)
{
_servoController.InvertTiltAxis = InvertTiltMenuItem.IsChecked;
_settings.InvertTiltAxis = InvertTiltMenuItem.IsChecked;
SaveSettings();
UpdateStatus($"Tilt axis inversion: {(InvertTiltMenuItem.IsChecked ? "Enabled" : "Disabled")}");
}
}
private void AboutMenuItem_Click(object sender, RoutedEventArgs e)
{
var aboutWindow = new AboutWindow();
aboutWindow.Owner = this;
aboutWindow.ShowDialog();
}
#endregion
#region UI Updates
private void UpdateStatus(string message)
{
// Log to console instead of UI
var timestamp = DateTime.Now.ToString("HH:mm:ss");
Console.WriteLine($"[{timestamp}] {message}");
}
private void UpdateServoStatusVisibility()
{
var usbConnected = _serialPortManager?.IsConnected ?? false;
var panConnected = _servoController?.IsPanServoConnected ?? false;
var tiltConnected = _servoController?.IsTiltServoConnected ?? false;
// Only show servo status if USB is connected AND at least one servo has issues
var showPanStatus = usbConnected && !panConnected;
var showTiltStatus = usbConnected && !tiltConnected;
PanStatusIndicator.Visibility = showPanStatus ? Visibility.Visible : Visibility.Collapsed;
PanStatusText.Visibility = showPanStatus ? Visibility.Visible : Visibility.Collapsed;
TiltStatusIndicator.Visibility = showTiltStatus ? Visibility.Visible : Visibility.Collapsed;
TiltStatusText.Visibility = showTiltStatus ? Visibility.Visible : Visibility.Collapsed;
}
private void UpdateControlButtonStates()
{
var panConnected = _servoController?.IsPanServoConnected ?? false;
var tiltConnected = _servoController?.IsTiltServoConnected ?? false;
var anyConnected = panConnected || tiltConnected;
PanLeftButton.IsEnabled = panConnected;
PanRightButton.IsEnabled = panConnected;
TiltUpButton.IsEnabled = tiltConnected;
TiltDownButton.IsEnabled = tiltConnected;
CenterButton.IsEnabled = anyConnected;
}
#endregion
#region Window Events
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Dispose();
}
#endregion
#region IDisposable
public void Dispose()
{
if (!_disposed)
{
_servoController?.Dispose();
_serialPortManager?.Dispose();
_disposed = true;
}
GC.SuppressFinalize(this);
}
~MainWindow()
{
Dispose();
}
#endregion
}
}