using System.Drawing; using System.Windows.Forms; using VisionBuilder.UI.IOCommander.Interfaces; namespace VisionBuilder.UI.IOCommander.Windows { public partial class WindowsIOCommanderDebugViewService : Form, IIOCommanderDebugViewService { private readonly WindowsIOCommanderDebugViewSettings _settings; private readonly Dictionary _inputPins = new(); private readonly Dictionary _outputPins = new(); private readonly object _lockObject = new object(); private bool _isVisible = false; public WindowsIOCommanderDebugViewService(WindowsIOCommanderDebugViewSettings settings) { _settings = settings; InitializeComponent(); InitializePinDisplay(); } protected override void OnShown(EventArgs e) { this.TopMost = true; this.Focus(); this.TopMost = true; } private void InitializePinDisplay() { // Configure form properties this.Text = "IOCommander Debug View"; this.FormBorderStyle = FormBorderStyle.FixedToolWindow; this.TopMost = true; this.TopLevel = true; this.ShowInTaskbar = false; this.StartPosition = FormStartPosition.Manual; this.Location = new Point(50, 50); this.Size = new Size(400, 350); // Made wider and adjusted height for 10 rows this.BackColor = Color.FromArgb(64, 64, 64); this.MinimizeBox = false; this.MaximizeBox = false; // Create pin indicators for input pins 1-10 and output pins 1-10 (10 rows, 2 columns) int pinSize = 24; // Increased from 18 int spacing = 4; // Increased from 2 int centerX = 200; // Center the graphics in the wider form int startY = 15; // Slightly more margin // Get label dictionaries for quick lookup var inputLabelDict = _settings.InputPinLabels?.ToDictionary(pl => pl.Pin, pl => pl.Label) ?? new Dictionary(); var outputLabelDict = _settings.OutputPinLabels?.ToDictionary(pl => pl.Pin, pl => pl.Label) ?? new Dictionary(); for (int row = 0; row < 10; row++) // Changed from 20 to 10 { int inputPin = row + 1; // Input pins numbered 1-10 int outputPin = row + 1; // Output pins numbered 1-10 // Left column (input pins) var leftIndicator = new PinIndicator(inputPin, true) { Location = new Point(centerX - pinSize - spacing * 2, startY + (row * (pinSize + spacing))), Size = new Size(pinSize, pinSize) }; _inputPins[inputPin] = leftIndicator; this.Controls.Add(leftIndicator); // Right column (output pins) var rightIndicator = new PinIndicator(outputPin, false) { Location = new Point(centerX + spacing * 2, startY + (row * (pinSize + spacing))), Size = new Size(pinSize, pinSize) }; _outputPins[outputPin] = rightIndicator; this.Controls.Add(rightIndicator); // Input pin label (left side - label before pin number) string inputLabelText = inputLabelDict.ContainsKey(inputPin) ? $"{inputLabelDict[inputPin]} {inputPin}" : inputPin.ToString(); var leftLabel = new Label { Text = inputLabelText, Location = new Point(5, startY + (row * (pinSize + spacing)) + 4), // Adjusted vertical alignment Size = new Size(centerX - pinSize - spacing * 3 - 5, 16), // Increased height from 14 Font = new Font("Arial", 8, FontStyle.Bold), // Increased from 6 ForeColor = Color.White, BackColor = Color.Transparent, TextAlign = ContentAlignment.MiddleRight }; this.Controls.Add(leftLabel); // Output pin label (right side - pin number followed by label) string outputLabelText = outputLabelDict.ContainsKey(outputPin) ? $"{outputPin} {outputLabelDict[outputPin]}" : outputPin.ToString(); var rightLabel = new Label { Text = outputLabelText, Location = new Point(centerX + pinSize + spacing * 3, startY + (row * (pinSize + spacing)) + 4), // Adjusted vertical alignment Size = new Size(this.Width - (centerX + pinSize + spacing * 3) - 10, 16), // Increased height from 14 Font = new Font("Arial", 8, FontStyle.Bold), // Increased from 6 ForeColor = Color.White, BackColor = Color.Transparent, TextAlign = ContentAlignment.MiddleLeft }; this.Controls.Add(rightLabel); } } public void SetInputPinStatus(int pin, bool state) { if (this.InvokeRequired) { this.Invoke(new Action(() => SetInputPinStatus(pin, state))); return; } lock (_lockObject) { if (_inputPins.ContainsKey(pin)) { _inputPins[pin].SetState(state); } } } public void SetOutputPinStatus(int pin, bool state) { if (this.InvokeRequired) { this.Invoke(new Action(() => SetOutputPinStatus(pin, state))); return; } lock (_lockObject) { if (_outputPins.ContainsKey(pin)) { _outputPins[pin].SetState(state); } } } public void ShowDebugView() { if (this.InvokeRequired) { this.Invoke(new Action(ShowDebugView)); return; } lock (_lockObject) { if (!_isVisible) { this.Show(); _isVisible = true; } } } private class PinIndicator : Control { private readonly int _pinNumber; private readonly bool _isInput; private bool _state = false; private readonly SolidBrush _activeBrush; private readonly SolidBrush _inactiveBrush; public PinIndicator(int pinNumber, bool isInput) { _pinNumber = pinNumber; _isInput = isInput; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); // Input pins: Green when active, dark gray when inactive // Output pins: Red when active, dark gray when inactive if (_isInput) { _activeBrush = new SolidBrush(Color.Lime); _inactiveBrush = new SolidBrush(Color.DarkGray); } else { _activeBrush = new SolidBrush(Color.Red); _inactiveBrush = new SolidBrush(Color.DarkGray); } } public void SetState(bool state) { if (_state != state) { _state = state; this.Invalidate(); } } protected override void OnPaint(PaintEventArgs e) { var brush = _state ? _activeBrush : _inactiveBrush; e.Graphics.FillEllipse(brush, 0, 0, Width - 1, Height - 1); e.Graphics.DrawEllipse(Pens.Black, 0, 0, Width - 1, Height - 1); } protected override void Dispose(bool disposing) { if (disposing) { _activeBrush?.Dispose(); _inactiveBrush?.Dispose(); } base.Dispose(disposing); } } } }