diff --git a/App.config b/App.config new file mode 100644 index 0000000..49cc43e --- /dev/null +++ b/App.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Assets/logo.png b/Assets/logo.png new file mode 100644 index 0000000..4e1886d Binary files /dev/null and b/Assets/logo.png differ diff --git a/CramLink_v2.csproj b/CramLink_v2.csproj new file mode 100644 index 0000000..f8f6072 --- /dev/null +++ b/CramLink_v2.csproj @@ -0,0 +1,20 @@ + + + WinExe + net6.0-windows + true + enable + enable + C:\TestRepo\CramLink + app.manifest + + + + + + + + + + + \ No newline at end of file diff --git a/CramLink_v2.sln b/CramLink_v2.sln new file mode 100644 index 0000000..019e189 --- /dev/null +++ b/CramLink_v2.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CramLink_v2", "CramLink_v2.csproj", "{3C79CF75-0152-4443-A49F-EA94FF67D2B7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3C79CF75-0152-4443-A49F-EA94FF67D2B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C79CF75-0152-4443-A49F-EA94FF67D2B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C79CF75-0152-4443-A49F-EA94FF67D2B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C79CF75-0152-4443-A49F-EA94FF67D2B7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {87E85B4D-7A8D-49DB-88F0-AB28E404CE70} + EndGlobalSection +EndGlobal diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs new file mode 100644 index 0000000..7b41e2c --- /dev/null +++ b/MainForm.Designer.cs @@ -0,0 +1,126 @@ +using System.Windows.Forms; + +namespace CramLinkClientGUI +{ + public partial class MainForm + { + private System.ComponentModel.IContainer components = null; + + private ComboBox siteComboBox; + private Button connectButton; + private Label statusLabel; + private TextBox logBox; + private Label label1; + private Label label2; + private Label piStatus; + private Label plcStatus; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + components.Dispose(); + base.Dispose(disposing); + } + + private void InitializeComponent() + { + siteComboBox = new ComboBox(); + connectButton = new Button(); + statusLabel = new Label(); + logBox = new TextBox(); + label1 = new Label(); + label2 = new Label(); + piStatus = new Label(); + plcStatus = new Label(); + SuspendLayout(); + // + // siteComboBox + // + siteComboBox.DropDownStyle = ComboBoxStyle.DropDownList; + siteComboBox.FormattingEnabled = true; + siteComboBox.Location = new Point(12, 12); + siteComboBox.Name = "siteComboBox"; + siteComboBox.Size = new Size(300, 23); + siteComboBox.TabIndex = 0; + // + // connectButton + // + connectButton.Location = new Point(330, 12); + connectButton.Name = "connectButton"; + connectButton.Size = new Size(100, 23); + connectButton.TabIndex = 1; + connectButton.Text = "Connect"; + connectButton.Click += connectButton_Click; + // + // statusLabel + // + statusLabel.AutoSize = true; + statusLabel.Location = new Point(450, 17); + statusLabel.Name = "statusLabel"; + statusLabel.Size = new Size(79, 15); + statusLabel.TabIndex = 2; + statusLabel.Text = "Disconnected"; + // + // logBox + // + logBox.Location = new Point(12, 50); + logBox.Multiline = true; + logBox.Name = "logBox"; + logBox.ReadOnly = true; + logBox.ScrollBars = ScrollBars.Vertical; + logBox.Size = new Size(600, 300); + logBox.TabIndex = 3; + // + // label1 + // + label1.Location = new Point(12, 360); + label1.Name = "label1"; + label1.Size = new Size(59, 23); + label1.TabIndex = 4; + label1.Text = "Pi Status:"; + // + // label2 + // + label2.Location = new Point(101, 360); + label2.Name = "label2"; + label2.Size = new Size(70, 23); + label2.TabIndex = 6; + label2.Text = "PLC Status:"; + // + // piStatus + // + piStatus.Font = new Font("Segoe UI Emoji", 12F, FontStyle.Regular, GraphicsUnit.Point); + piStatus.Location = new Point(65, 358); + piStatus.Name = "piStatus"; + piStatus.Size = new Size(30, 23); + piStatus.TabIndex = 5; + piStatus.Text = "🔴"; + // + // plcStatus + // + plcStatus.Font = new Font("Segoe UI Emoji", 12F, FontStyle.Regular, GraphicsUnit.Point); + plcStatus.Location = new Point(168, 358); + plcStatus.Name = "plcStatus"; + plcStatus.Size = new Size(31, 23); + plcStatus.TabIndex = 7; + plcStatus.Text = "🔴"; + // + // MainForm + // + ClientSize = new Size(630, 390); + Controls.Add(siteComboBox); + Controls.Add(connectButton); + Controls.Add(statusLabel); + Controls.Add(logBox); + Controls.Add(label1); + Controls.Add(piStatus); + Controls.Add(label2); + Controls.Add(plcStatus); + Name = "MainForm"; + Text = "CramLink VPN Client"; + ResumeLayout(false); + PerformLayout(); + } + } +} + diff --git a/MainForm.cs b/MainForm.cs new file mode 100644 index 0000000..658d5fb --- /dev/null +++ b/MainForm.cs @@ -0,0 +1,111 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Net.NetworkInformation; +using System.Windows.Forms; + +namespace CramLinkClientGUI +{ + public partial class MainForm : Form + { + private Process vpnProcess; + private System.Windows.Forms.Timer pingTimer = new System.Windows.Forms.Timer(); + + public MainForm() + { + InitializeComponent(); + + statusLabel.Text = "Disconnected"; + siteComboBox.Items.AddRange(new[] + { + "CramLink 1 - HQ", + "CramLink 2 - N/A" + }); + siteComboBox.SelectedIndex = 0; + + pingTimer.Interval = 5000; + pingTimer.Tick += PingTargets; + } + + private void connectButton_Click(object sender, EventArgs e) + { + if (vpnProcess != null && !vpnProcess.HasExited) + { + vpnProcess.Kill(); + vpnProcess.Dispose(); + vpnProcess = null; + statusLabel.Text = "Disconnected"; + connectButton.Text = "Connect"; + logBox.AppendText("VPN disconnected.\r\n"); + pingTimer.Stop(); + return; + } + + string configPath = "C:\\CramLinkVPN\\client.ovpn"; + string openvpnPath = "C:\\CramLinkVPN\\openvpn.exe"; + + if (!File.Exists(configPath) || !File.Exists(openvpnPath)) + { + MessageBox.Show("OpenVPN executable or config not found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + vpnProcess = new Process(); + vpnProcess.StartInfo.FileName = openvpnPath; + vpnProcess.StartInfo.Arguments = "--config \"" + configPath + "\""; + vpnProcess.StartInfo.RedirectStandardOutput = true; + vpnProcess.StartInfo.RedirectStandardError = true; + vpnProcess.StartInfo.UseShellExecute = false; + vpnProcess.StartInfo.CreateNoWindow = true; + + vpnProcess.OutputDataReceived += (s, args) => HandleVPNOutput(args.Data); + vpnProcess.ErrorDataReceived += (s, args) => HandleVPNOutput(args.Data); + + vpnProcess.Start(); + vpnProcess.BeginOutputReadLine(); + vpnProcess.BeginErrorReadLine(); + + connectButton.Text = "Disconnect"; + statusLabel.Text = "Connecting..."; + logBox.AppendText("Connecting to VPN...\r\n"); + } + + private void HandleVPNOutput(string data) + { + if (data == null) return; + Invoke(new Action(() => + { + logBox.AppendText(data + "\r\n"); + if (data.Contains("Initialization Sequence Completed")) + { + statusLabel.Text = "Connected"; + pingTimer.Start(); + } + })); + } + + private void PingTargets(object sender, EventArgs e) + { + bool piOnline = PingHost("192.168.0.53"); + bool plcOnline = PingHost("192.168.0.98"); + piStatus.ForeColor = piOnline ? System.Drawing.Color.Green : System.Drawing.Color.Red; + plcStatus.ForeColor = plcOnline ? System.Drawing.Color.Green : System.Drawing.Color.Red; + } + + private bool PingHost(string host) + { + try + { + using Ping ping = new Ping(); + PingReply reply = ping.Send(host, 1000); + return reply.Status == IPStatus.Success; + } + catch { return false; } + } + + private void plcStatus_Click(object sender, EventArgs e) + { + + } + } +} diff --git a/MainForm.resx b/MainForm.resx new file mode 100644 index 0000000..af32865 --- /dev/null +++ b/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..59415fb --- /dev/null +++ b/Program.cs @@ -0,0 +1,15 @@ +using System; +using System.Windows.Forms; + +namespace CramLinkClientGUI +{ + internal static class Program + { + [STAThread] + static void Main() + { + ApplicationConfiguration.Initialize(); + Application.Run(new SplashContext()); + } + } +} diff --git a/Properties/logo.png b/Properties/logo.png new file mode 100644 index 0000000..4e1886d Binary files /dev/null and b/Properties/logo.png differ diff --git a/SplashContext.cs b/SplashContext.cs new file mode 100644 index 0000000..ef6bc47 --- /dev/null +++ b/SplashContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Windows.Forms; +using System.Threading.Tasks; + +namespace CramLinkClientGUI +{ + public class SplashContext : ApplicationContext + { + private SplashForm splash; + private MainForm main; + + public SplashContext() + { + splash = new SplashForm(); + splash.Shown += async (s, e) => + { + await Task.Delay(2500); + splash.Hide(); + main = new MainForm(); + main.FormClosed += (s2, e2) => ExitThread(); + main.Show(); + }; + + splash.Show(); + } + } +} diff --git a/SplashForm.Designer.cs b/SplashForm.Designer.cs new file mode 100644 index 0000000..dc2d6bd --- /dev/null +++ b/SplashForm.Designer.cs @@ -0,0 +1,49 @@ +namespace CramLinkClientGUI +{ + partial class SplashForm + { + private System.ComponentModel.IContainer components = null; + private System.Windows.Forms.PictureBox logoBox; + + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + private void InitializeComponent() + { + this.logoBox = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.logoBox)).BeginInit(); + this.SuspendLayout(); + // + // logoBox + // + this.logoBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.logoBox.Location = new System.Drawing.Point(0, 0); + this.logoBox.Name = "logoBox"; + this.logoBox.Size = new System.Drawing.Size(768, 512); + this.logoBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.logoBox.TabIndex = 0; + this.logoBox.TabStop = false; + // + // SplashForm + // + this.Opacity = 0; + this.TopMost = true; + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(768, 512); + this.Controls.Add(this.logoBox); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "SplashForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "SplashForm"; + ((System.ComponentModel.ISupportInitialize)(this.logoBox)).EndInit(); + this.ResumeLayout(false); + } + } +} diff --git a/SplashForm.cs b/SplashForm.cs new file mode 100644 index 0000000..54bc134 --- /dev/null +++ b/SplashForm.cs @@ -0,0 +1,42 @@ +using System; +using System.Drawing; +using System.IO; +using System.Reflection; +using System.Windows.Forms; +using Timer = System.Windows.Forms.Timer; + +namespace CramLinkClientGUI +{ + public partial class SplashForm : Form + { + private Timer fadeTimer; + public SplashForm() + { + InitializeComponent(); + StartFadeIn(); + LoadLogo(); + } + private void StartFadeIn() + { + this.Opacity = 0; + fadeTimer = new Timer(); + fadeTimer.Interval = 30; // ms + fadeTimer.Tick += (s, e) => + { + if (this.Opacity < 1) + this.Opacity += 0.05; + else + fadeTimer.Stop(); + }; + fadeTimer.Start(); + } + + private void LoadLogo() + { + var assembly = Assembly.GetExecutingAssembly(); + using Stream stream = assembly.GetManifestResourceStream("CramLink_v2.Assets.logo.png"); + if (stream != null) + logoBox.Image = Image.FromStream(stream); + } + } +} \ No newline at end of file diff --git a/SplashForm.resx b/SplashForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/SplashForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/app.manifest b/app.manifest new file mode 100644 index 0000000..fcd4341 --- /dev/null +++ b/app.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + +