using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Media; using TradeIdeas.ServerConnection; namespace TclShell { public partial class MainForm : Form, IConnectionNameListener { /// /// This will be null if we don't know the channel. Possibly there was an error. /// Possibly we're just waiting for the response from the server. /// public static string ResponseChannel { get; private set; } public MainForm() { InitializeComponent(); virtualNameComboBox.Items.AddRange(DESTINATIONS); string[] args = Environment.GetCommandLineArgs(); if (args.Length >= 2) { hostNameTextBox.Text = args[1]; if (args.Length >= 3) portTextBox.Text = args[2]; } UpdateComboBox(); } // public static string ConnectionName { get; private set; } private void connectButton_Click(object sender, EventArgs e) { int port; if (Int32.TryParse(portTextBox.Text, out port)) { ServerConnectionHolder.Connect(hostNameTextBox.Text, port); // Listen for the old type of background messages. var msg = TalkWithServer.CreateMessage("command", "flex_listen"); ServerConnectionHolder.Connection.SendMessage(msg, FlexResponse, true); // Listen for the new type of background messages. initBackgroundChannel(); ConnectionName = CurrentDestination().Name; foreach (var window in Application.OpenForms) if (window is IConnectionNameListener) ((IConnectionNameListener)window).OnNewConnectionName(ConnectionName); } else SystemSounds.Beep.Play(); } private void pingCheckBox_CheckedChanged(object sender, EventArgs e) { pingTimer.Enabled = pingCheckBox.Checked; } private Dictionary _lastMessage; private void pingTimer_Tick(object sender, EventArgs e) { if (!ServerConnectionHolder.Connected) { pingStatusLabel.Text = "Not connected."; return; } _lastMessage = TalkWithServer.CreateMessage("command", "tcl_command", "script", "expr 1"); ServerConnectionHolder.Connection.SendMessage(_lastMessage, PingResponse, clientId: _lastMessage); } private void PingResponse(byte[] body, object clientId) { if (InvokeRequired) Invoke((MethodInvoker)delegate { PingResponse(body, clientId); }); else { if (clientId == _lastMessage) { // Ignore old messages. if (null == body) pingStatusLabel.Text = "Error " + DateTime.Now.ToString("H:mm:ss"); else pingStatusLabel.Text = "Good " + DateTime.Now.ToString("H:mm:ss"); } } } private void FlexResponse(byte[] body, object clientId) { if (InvokeRequired) Invoke((MethodInvoker)delegate { FlexResponse(body, clientId); }); else { if (null == body) flexResponseTextBox.AppendText("Error.\r\n"); else flexResponseTextBox.AppendText(TalkWithServer.BytesToStr(body).Replace("\n", "\r\n") + "\r\n"); } } private void newWindowButton_Click(object sender, EventArgs e) { new ScriptExecutor().Show(); } private void MainForm_Resize(object sender, EventArgs e) { int space = backgroundChannelNameTextBox.Top - flexResponseTextBox.Bottom; int usableWidth = flexResponseTextBox.Width - space; try { backgroundChannelNameTextBox.Width = usableWidth / 2; backgroundChannelScriptTextBox.Width = usableWidth - backgroundChannelNameTextBox.Width; backgroundChannelScriptTextBox.Left = backgroundChannelNameTextBox.Right + space; } catch { } } private TalkWithServer.CancelToken _backgroundChannelCancelToken = null; private object _sessionId; bool _backgroundChannelFirstMessage; private void initBackgroundChannel() { ShowDisconnected(); if (null != _backgroundChannelCancelToken) _backgroundChannelCancelToken.Cancel(); _backgroundChannelCancelToken = new TalkWithServer.CancelToken(); var message = TalkWithServer.CreateMessage("command", "tcl_command", "script", "namespace eval [ti::get_socket_temp_ns]::tcl_shell {variable channel [ti::get_current_channel]; set channel}"); _sessionId = new object(); _backgroundChannelFirstMessage = true; ServerConnectionHolder.Connection.SendMessage(message, Response, streaming: true, clientId: _sessionId, cancelToken: _backgroundChannelCancelToken); } private void Response(byte[] body, object clientId) { BeginInvoke((MethodInvoker)delegate { ResponseInThread(body, clientId); }); } private void ResponseInThread(byte[] body, object clientId) { if (clientId != _sessionId) // Ignore old messages; return; if (null == body) ShowDisconnected(); else { String response = TalkWithServer.BytesToStr(body); if (_backgroundChannelFirstMessage) { _backgroundChannelFirstMessage = false; if (response.StartsWith("0: ")) { backgroundChannelNameTextBox.Enabled = true; backgroundChannelNameTextBox.Text = response.Substring(3); ResponseChannel = backgroundChannelNameTextBox.Text; } else { // Presumably we got an error message. backgroundChannelNameTextBox.Enabled = false; backgroundChannelNameTextBox.Text = response; ResponseChannel = null; } } else flexResponseTextBox.AppendText(response.Replace("\n", "\r\n") + "\r\n"); } } private void ShowDisconnected() { backgroundChannelNameTextBox.Enabled = false; backgroundChannelNameTextBox.Text = "disconnected"; ResponseChannel = null; } private class Destination { /// /// Visible to the user in the combo box. /// public String Name { get; private set; } /// /// Can be null. Null means we don't change the GUI when someone /// selected this. Null means we won't Match() the GUI ever. /// public String HostName {get; private set; } /// /// Can be null. Null means we don't change the GUI when someone /// selected this. Null means we won't Match() the GUI ever. /// public String PortNumber { get; private set; } public Destination(String name, String hostName, String portNumber) { Name = name; HostName = hostName; PortNumber = portNumber; } public Destination(String name) { Name = name; } public override string ToString() { return Name; } public void DisplayTo(MainForm mainForm) { if (null != HostName) mainForm.hostNameTextBox.Text = HostName; if (null != PortNumber) mainForm.portTextBox.Text = PortNumber; } public bool Matches(MainForm mainForm) { return (HostName == mainForm.hostNameTextBox.Text) && (PortNumber == mainForm.portTextBox.Text); } } // I considered a GUI to let you change this. But this seems sufficient. // These are usually hiding behind a firewall, so you see 127.0.0.1 a lot. // Make sure your SSH forwarding settings match these names. private static readonly Destination[] DavesDestinations = new Destination[] { new Destination("Custom"), new Destination("Linode test", "127.0.0.1", "9372") }; private static readonly Destination[] PhilipsDestinations = new Destination[] { new Destination("Custom"), new Destination("═══ Live ═══════════════"), new Destination("Lrrr (live AI)", "127.0.0.1", "9370"), // 9370 --> lrrr:9370 new Destination("pc-load-letter (AI Server)", "127.0.0.1", "9383"), // 9383 --> pc-load-letter:9270 new Destination("y2k (AI Server)", "127.0.0.1", "9380"), // 9380 --> y2k:9270 new Destination("───────────────"), new Destination("Sinclair-2k (live charts)", "127.0.0.1", "9373"), // 9373 --> sinclair-2k:9370 new Destination("Daisy-Mae-128k (live charts)", "127.0.0.1", "9374"), // 9374 --> daisy-mae-128k:9370 new Destination("y2k (live charts)", "127.0.0.1", "9381"), // 9381 --> y2k:9370 new Destination("dom (live charts, on its way out)", "127.0.0.1", "9382"), // 9382 --> dom:9370 new Destination("pc-load-letter (live charts)", "127.0.0.1", "9384"), // 9384 --> pc-load-letter:9370 new Destination("null-pointer (live charts)", "127.0.0.1", "9385"), // 9385 --> null-pointer:9370 new Destination("───────────────"), new Destination("null-pointer (live Paper Trading)", "127.0.0.1", "9387"), // 9387 --> null-pointer:9270 new Destination("bob-saget (live calendar)", "127.0.0.1", "9389"), // 9389 --> bob-saget:9270 new Destination("chuck-liddell (live charts)", "127.0.0.1", "9390"), // 9390 --> chuck-liddell:9370 new Destination("race-condition (live charts)", "127.0.0.1", "9391"), // 9391 --> race-condition:9370 new Destination("seth (market explorer server)", "127.0.0.1", "9393"), // 9393 --> seth:9370 new Destination("Ndnd (misc)", "127.0.0.1", "9376"), // 9376 --> ndnd:9370 new Destination("Sinclair-2k (price alerts)", "127.0.0.1", "9375"), // 9375 --> sinclair-2k:9270 new Destination("═══ Test and Development ════"), new Destination("Becca (test)", "127.0.0.1", "9371"), // 9371 --> becca:9370 new Destination("Linode test", "127.0.0.1", "9372"), // 9372 --> ??:9370 new Destination("Morbo (test AI)", "127.0.0.1", "9377"), // 9377 --> morbo:9370 new Destination("Morbo (dev AI)", "127.0.0.1", "9392"), // 9392 --> morbo:9270 new Destination("Morbo (dev Paper Trading)", "127.0.0.1", "9386"), // 9386 --> morbo:9170 new Destination("id-10-t (24 hour)", "127.0.0.1", "9378"), // 9378 --> 127.0.0.1:9370 new Destination("Joey-Mousepad (Philip dev)", "127.0.0.1", "9379"), // 9379 --> joey-mousepad:9370 new Destination("Morbo (Tim Dev Instance)", "127.0.0.1", "9394"), // 9394 --> morbo:9670 }; private static readonly Destination[] DESTINATIONS = PhilipsDestinations; private static readonly Destination DEFAULT_DESTINATION = DESTINATIONS[0]; private bool _ignoreAddressChanges = false; private void hostNameTextBox_TextChanged(object sender, EventArgs e) { if (_ignoreAddressChanges) return; UpdateComboBox(); } private void virtualNameComboBox_SelectedValueChanged(object sender, EventArgs e) { if (_ignoreAddressChanges) return; UpdateFromComboBox(); } private void UpdateComboBox() { _ignoreAddressChanges = true; virtualNameComboBox.SelectedItem = DEFAULT_DESTINATION; foreach (Destination destination in DESTINATIONS) if (destination.Matches(this)) { virtualNameComboBox.SelectedItem = destination; break; } _ignoreAddressChanges = false; } private Destination CurrentDestination() { return (Destination)virtualNameComboBox.SelectedItem; } private void UpdateFromComboBox() { _ignoreAddressChanges = true; CurrentDestination().DisplayTo(this); _ignoreAddressChanges = false; } void IConnectionNameListener.OnNewConnectionName(string connectionName) { Text = "TCL Shell – " + connectionName; } private void reloaderButton_Click(object sender, EventArgs e) { new ReloadTcl().Show(); } } }