using Google.Protobuf; using Google.Protobuf.Collections; using Microsoft.Win32; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Threading; using Tango.Core; using Tango.Core.Commands; using Tango.Integration.Operation; using Tango.PMR; using Tango.Scripting; using Tango.Settings; using Tango.SharedUI; using Tango.Stubs.Views; using Tango.Transport; using Tango.Transport.Adapters; namespace Tango.Stubs.ViewModels { /// /// Represents the script execution utility main view model. /// /// public class StubsViewVM : ViewModel { private StubManager _stubManager; private TextBox _logTextBox; private StubsSettings _settings; private Core.Threading.IntervalMessageDispatcher _consoleDispatcher; #region Properties public ITransportAdapter OverrideAdapter { get; set; } public List CreateGroups { get; set; } public List Examples { get; set; } private IMachineOperator _machineOperator; /// /// Gets or sets the machine operator. /// public IMachineOperator MachineOperator { get { return _machineOperator; } set { _machineOperator = value; RaisePropertyChangedAuto(); } } /// /// Gets or sets the code tabs. /// public ObservableCollection CodeTabs { get; set; } /// /// Gets or sets the additional highlight C# types. /// public ObservableCollection> HighlightTypes { get; set; } /// /// Gets or sets the intellisense types. /// public ObservableCollection> IntellisenseTypes { get; set; } /// /// Gets or sets the collection of stub snippets. /// public ObservableCollection StubSnippets { get; set; } private StubSnippetVM _selectedStubSnippet; /// /// Gets or sets the selected stub snippet. /// public StubSnippetVM SelectedStubSnippet { get { return _selectedStubSnippet; } set { _selectedStubSnippet = value; RaisePropertyChanged(nameof(SelectedStubSnippet)); } } private CodeTabVM _selectedCodeTab; /// /// Gets or sets the selected code tab. /// public CodeTabVM SelectedCodeTab { get { return _selectedCodeTab; } set { _selectedCodeTab = value; RaisePropertyChanged(nameof(SelectedCodeTab)); InvalidateRelayCommands(); } } private bool _isConnected; /// /// Gets or sets a value indicating whether the USB adapter is connected. /// public bool IsConnected { get { return _isConnected; } set { _isConnected = value; RaisePropertyChanged(nameof(IsConnected)); InvalidateRelayCommands(); } } private List _ports; /// /// Gets or sets the available USB ports. /// public List Ports { get { return _ports; } set { _ports = value; RaisePropertyChanged(nameof(Ports)); } } private String _selectedPort; /// /// Gets or sets the selected USB port. /// public String SelectedPort { get { return _selectedPort; } set { _selectedPort = value; RaisePropertyChanged(nameof(SelectedPort)); InvalidateRelayCommands(); } } private String _status; /// /// Gets or sets the current status bar text. /// public String Status { get { return _status; } set { _status = value; RaisePropertyChanged(nameof(Status)); } } private bool _isRunning; /// /// Gets or sets a value indicating whether a stub is currently running. /// public bool IsRunning { get { return _isRunning; } set { _isRunning = value; RaisePropertyChanged(nameof(IsRunning)); InvalidateRelayCommands(); } } private bool _appendLogAuto; /// /// Gets or sets a value indicating whether the logs automatically. /// public bool AppendLogAuto { get { return _appendLogAuto; } set { _appendLogAuto = value; RaisePropertyChangedAuto(); } } private UsbSerialBaudRates _baudRate; /// /// Gets or sets the baud rate. /// public UsbSerialBaudRates BaudRate { get { return _baudRate; } set { _baudRate = value; RaisePropertyChangedAuto(); } } private ConnectionMode _connectionMode; /// /// Gets or sets the connection mode. /// public ConnectionMode ConnectionMode { get { return _connectionMode; } set { _connectionMode = value; RaisePropertyChangedAuto(); } } private bool _displayConnectionPane; /// /// Gets or sets a value indicating whether [hide connection pane]. /// public bool DisplayConnectionPane { get { return _displayConnectionPane; } set { _displayConnectionPane = value; RaisePropertyChangedAuto(); } } #endregion #region Commands /// /// Gets or sets the new command. /// public RelayCommand NewCommand { get; set; } /// /// Gets or sets the close tab command. /// public RelayCommand CloseTabCommand { get; set; } /// /// Gets or sets the build command. /// public RelayCommand BuildCommand { get; set; } /// /// Gets or sets the run command. /// public RelayCommand RunCommand { get; set; } /// /// Gets or sets the stop command. /// public RelayCommand StopCommand { get; set; } /// /// Gets or sets the toggle connection command. /// public RelayCommand ToggleConnectionCommand { get; set; } /// /// Gets or sets the open command. /// public RelayCommand OpenCommand { get; set; } /// /// Gets or sets the save command. /// public RelayCommand SaveCommand { get; set; } /// /// Gets or sets the save as command. /// public RelayCommand SaveAsCommand { get; set; } /// /// Gets or sets the clear command. /// public RelayCommand ClearCommand { get; set; } /// /// Gets or sets the stub snippet selected command. /// public RelayCommand StubSnippetSelectedCommand { get; set; } /// /// Gets or sets the insert snippet command. /// public RelayCommand InsertSnippetCommand { get; set; } /// /// Gets or sets the create item command. /// public RelayCommand CreateItemCommand { get; set; } /// /// Gets or sets the create example command. /// public RelayCommand CreateExampleCommand { get; set; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public StubsViewVM() { _consoleDispatcher = new Core.Threading.IntervalMessageDispatcher(OnConsoleLog); _consoleDispatcher.Start(); DisplayConnectionPane = true; _settings = SettingsManager.Default.GetOrCreate(); Examples = new List(); CodeTabs = new ObservableCollection(); NewCommand = new RelayCommand(CreateNewTab); CloseTabCommand = new RelayCommand(OnTabClosing); RunCommand = new RelayCommand(RunTab, (x) => IsConnected && !IsRunning && SelectedCodeTab != null); BuildCommand = new RelayCommand(async () => await BuildTab(), (x) => !IsRunning && SelectedCodeTab != null); StopCommand = new RelayCommand(StopTab, (x) => IsConnected && IsRunning && SelectedCodeTab != null); InsertSnippetCommand = new RelayCommand((x) => { }); CreateExampleCommand = new RelayCommand(CreateExample); HighlightTypes = new ObservableCollection>(); IntellisenseTypes = new ObservableCollection>(); IntellisenseTypes.Add(new KeyValuePair("stubManager", typeof(StubManager))); foreach (var stubType in typeof(PMR.Common.MessageContainer).Assembly.GetTypes().Where(x => typeof(IMessage).IsAssignableFrom(x))) { HighlightTypes.Add(new KeyValuePair(stubType.Name, stubType)); } HighlightTypes.Add(new KeyValuePair("Thread", typeof(Thread))); HighlightTypes.Add(new KeyValuePair("DateTime", typeof(DateTime))); HighlightTypes.Add(new KeyValuePair("TimeSpan", typeof(TimeSpan))); HighlightTypes.Add(new KeyValuePair("Dispatcher", typeof(Dispatcher))); HighlightTypes.Add(new KeyValuePair("Task", typeof(Task))); HighlightTypes.Add(new KeyValuePair("List", typeof(IList))); HighlightTypes.Add(new KeyValuePair("int", typeof(Int32))); HighlightTypes.Add(new KeyValuePair("double", typeof(Double))); HighlightTypes.Add(new KeyValuePair("String", typeof(String))); HighlightTypes.Add(new KeyValuePair("string", typeof(String))); foreach (var item in HighlightTypes) { IntellisenseTypes.Add(item); } StubSnippets = new ObservableCollection(); foreach (var stubType in MessageFactory.GetAvailableRequestStubs()) { StubSnippetVM snippet = new StubSnippetVM(); snippet.Name = stubType.Name.Replace("Stub", "").Replace("Request", "").ToWords(); snippet.Code = String.Empty; snippet.Code += "// " + "Request ----" + Environment.NewLine; foreach (var prop in stubType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { snippet.Code += "// " + prop.PropertyType.Name + " : " + prop.Name + Environment.NewLine; } Type responseType = MessageFactory.GetAvailableRequestResponseStubs().SingleOrDefault(x => x.Name == stubType.Name.Replace("Request", "Response")); if (responseType != null) { snippet.Code += Environment.NewLine + "// " + "Response ----" + Environment.NewLine; foreach (var prop in responseType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { snippet.Code += "// " + prop.PropertyType.Name + " : " + prop.Name + Environment.NewLine; } } snippet.Code += String.Format("var response = stubManager.Run<{2}>(\"{0}\" ,{1});", stubType.Name, String.Join(", ", stubType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(x => x.PropertyType.Name == "string" ? "\"string\"" : x.PropertyType.Name)), stubType.Name.Replace("Request", "Response")); StubSnippets.Add(snippet); } ToggleConnectionCommand = new RelayCommand(ToggleConnection, (x) => !IsRunning); OpenCommand = new RelayCommand(OpenFile); SaveCommand = new RelayCommand(SaveFile); SaveAsCommand = new RelayCommand(SaveAsFile); StubSnippetSelectedCommand = new RelayCommand(OnStubSnippetSelected); ClearCommand = new RelayCommand(ClearLog); Ports = new List(); for (int i = 1; i < 100; i++) { Ports.Add("COM" + i); } SelectedPort = _settings.SelectedPort != null ? _settings.SelectedPort : Ports.First(); BaudRate = _settings.BaudRate; AppendLogAuto = _settings.AutoLogResponse; Status = "Ready"; if (_settings.LastTabs.Count > 0) { foreach (var file in _settings.LastTabs) { if (File.Exists(file)) { OpenFile(file); } } } else { CreateNewTab(); } CreateGroups = new List(); foreach (var typesGroup in typeof(PMR.Common.MessageContainer).Assembly.GetTypes().Where(x => x.IsClass && !x.IsGenericType && !x.Name.Contains("Reflection") && typeof(IMessage).IsAssignableFrom(x)).GroupBy(x => x.Namespace)) { CreateGroupVM group = new CreateGroupVM(); group.Name = typesGroup.First().Namespace.Split('.').Last(); foreach (var type in typesGroup) { group.Items.Add(new CreateItemVM() { Name = type.Name, Type = type, }); } CreateGroups.Add(group); } CreateItemCommand = new RelayCommand(CreateItem); foreach (var name in typeof(StubsViewVM).Assembly.GetManifestResourceNames()) { if (name.Contains(".Examples.")) { using (Stream stream = typeof(StubsViewVM).Assembly.GetManifestResourceStream(name)) { StreamReader reader = new StreamReader(stream); ExampleVM example = new ExampleVM(); String[] str = name.Split('.'); example.Name = str[str.Length - 2].ToWords(); example.Code = reader.ReadToEnd(); Examples.Add(example); } } } Examples = Examples.OrderBy(x => x.Name).ToList(); } private void OnConsoleLog(String log) { InvokeUINow(() => { if (_logTextBox.Text.Length > 99999) { _logTextBox.Clear(); } _logTextBox.AppendText(log); }); } public StubsViewVM(ConnectionMode connectionMode) : this() { ConnectionMode = connectionMode; if (ConnectionMode == ConnectionMode.External) { IsConnected = true; DisplayConnectionPane = false; } } #endregion #region Virtual Methods /// /// Called when a stub snippet is double clicked. /// protected virtual void OnStubSnippetSelected() { if (SelectedStubSnippet != null) { if (InsertSnippetCommand != null) { InsertSnippetCommand.Execute(SelectedStubSnippet.Code); } } } /// /// Called when user closes a script tab. /// /// The code tab. protected virtual void OnTabClosing(CodeTabVM codeTab) { CodeTabs.Remove(codeTab); } #endregion #region Private Methods private void CreateExample(ExampleVM example) { CreateNewTab(); SelectedCodeTab.Code = example.Code; SelectedCodeTab.Title = example.Name; } private void CreateItem(CreateItemVM item) { if (item != null) { if (InsertSnippetCommand != null) { String code = String.Empty; FormatProperties(item.Type, ref code); InsertSnippetCommand.Execute(code); } } } private void FormatProperties(Type type, ref String code) { code += Environment.NewLine + String.Format("{0} {1} = new {0}();", type.Name, type.Name.ToCamelCase()) + Environment.NewLine; foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { if (prop.PropertyType == typeof(String)) { code += String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, "null") + Environment.NewLine; } else if (prop.PropertyType.IsEnum) { code += String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, Activator.CreateInstance(prop.PropertyType).GetType().FullName + "." + Activator.CreateInstance(prop.PropertyType).ToString()) + Environment.NewLine; } else if (!prop.PropertyType.IsClass) { code += String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, Activator.CreateInstance(prop.PropertyType).ToString().ToLower()) + Environment.NewLine; } else if (prop.PropertyType.IsGenericType) { Type genericType = prop.PropertyType.GenericTypeArguments[0]; FormatProperties(genericType, ref code); code += String.Format("{0}.{1}.Add({2});", type.Name.ToCamelCase(), prop.Name, genericType.Name.ToCamelCase()) + Environment.NewLine; } else { FormatProperties(prop.PropertyType, ref code); code += Environment.NewLine + String.Format("{0}.{1} = {2};", type.Name.ToCamelCase(), prop.Name, prop.Name.ToCamelCase()) + Environment.NewLine; } } } /// /// Clears the log. /// private void ClearLog() { _logTextBox.Clear(); } /// /// Saves the selected script file. /// private async void SaveFile() { if (SelectedCodeTab != null) { if (SelectedCodeTab.File == null) { SaveAsFile(); } else { Status = "Saving " + SelectedCodeTab.File + "..."; File.WriteAllText(SelectedCodeTab.File, SelectedCodeTab.Code); await Task.Delay(1000); Status = "Ready"; SaveSettings(); } } } /// /// Saves the selected script file. /// private async void SaveAsFile() { if (SelectedCodeTab != null) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "C# Script Files|*.cs"; dlg.DefaultExt = ".cs"; if (dlg.ShowDialog().Value) { Status = "Saving " + dlg.FileName + "..."; File.WriteAllText(dlg.FileName, SelectedCodeTab.Code); SelectedCodeTab.File = dlg.FileName; await Task.Delay(1000); Status = "Ready"; SaveSettings(); } } } public void SaveSettings() { _settings.AutoLogResponse = AppendLogAuto; _settings.SelectedPort = SelectedPort; _settings.BaudRate = BaudRate; _settings.LastTabs = CodeTabs.Where(x => x.File != null).Select(x => x.File).ToList(); _settings.Save(); } /// /// Opens a script from HD. /// private void OpenFile() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "C# Script Files|*.cs"; dlg.Multiselect = true; if (dlg.ShowDialog().Value) { foreach (var file in dlg.FileNames) { OpenFile(file); } } } /// /// Opens the file. /// /// The file. private void OpenFile(String file) { var newTab = new CodeTabVM(); newTab.File = file; newTab.Code = File.ReadAllText(file); CodeTabs.Add(newTab); SelectedCodeTab = newTab; } /// /// Toggles the USB adapter connection. /// private async void ToggleConnection() { try { if (!IsConnected) { Mouse.OverrideCursor = Cursors.Wait; AppendTextLog("Connecting..." + Environment.NewLine); Integration.Operation.MachineOperator.EnableSessionLogFile = false; _machineOperator = new MachineOperator(); _machineOperator.EnableDiagnostics = false; _machineOperator.EnableEmbeddedDebugging = false; _machineOperator.EnableEventsNotification = false; _machineOperator.EnableJobResume = false; _machineOperator.UseKeepAlive = false; _machineOperator.Adapter = new UsbTransportAdapter(SelectedPort, BaudRate); await _machineOperator.Connect(); IsConnected = true; AppendTextLog("Connected." + Environment.NewLine); Mouse.OverrideCursor = null; } else { AppendTextLog("Disconnecting..." + Environment.NewLine); IsConnected = false; await _machineOperator.Disconnect(); AppendTextLog("Disconnected." + Environment.NewLine); } } catch (Exception ex) { AppendTextLog(ex.Message + Environment.NewLine); } finally { Mouse.OverrideCursor = null; } } /// /// Creates a new script tab. /// private void CreateNewTab() { var newTab = new CodeTabVM(); CodeTabs.Add(newTab); SelectedCodeTab = newTab; } /// /// Runs the selected script tab. /// private async void RunTab() { await BuildTab(); if (SelectedCodeTab.Errors.Count > 0) return; if (MachineOperator == null || MachineOperator.State != TransportComponentState.Connected) { AppendTextLog("Machine operator is not initialized or connected. Could not execute script." + Environment.NewLine); return; } IsRunning = true; SelectedCodeTab.IsRunning = true; _logTextBox.Text = (DateTime.Now.ToTimeString() + ": ") + "Executing script '" + SelectedCodeTab.Title + "'..." + Environment.NewLine; Status = "Running..."; await Task.Factory.StartNew(async () => { try { _stubManager = new StubManager(_machineOperator, (txt) => { AppendTextLog(txt + Environment.NewLine); }, (txt) => { AppendTextLog(txt); }, () => { ClearTextLog(); }); var thisStubManager = _stubManager; _stubManager.Completed += Manager_Completed; _stubManager.Failed += Manager_Failed; _stubManager.Executed += Manager_Executed; _stubManager.AutoLog = AppendLogAuto; ScriptEngine engine = new ScriptEngine(new StubOnExecuteParameters(_stubManager)); engine.ReferencedAssemblies.Add(this.GetType()); engine.ReferencedAssemblies.Add(typeof(PMR.Stubs.CalculateRequest)); engine.ReferencedAssemblies.Add(typeof(IMessage)); Status = $"Running '{SelectedCodeTab.Title}'..."; await engine.Run(SelectedCodeTab.Code, Path.GetDirectoryName(SelectedCodeTab.File)); if (!thisStubManager.Aborted) { IsRunning = false; SelectedCodeTab.IsRunning = false; Status = "Aboted"; } else { Status = "Completed"; } } catch (Exception ex) { IsRunning = false; SelectedCodeTab.IsRunning = false; } }); } /// /// Builds the tab. /// private Task BuildTab() { return Task.Factory.StartNew(() => { try { Status = "Compiling " + SelectedCodeTab.Title + "..."; var thisStubManager = _stubManager; ScriptEngine engine = new ScriptEngine(new StubOnExecuteParameters(_stubManager)); engine.ReferencedAssemblies.Add(this.GetType()); engine.ReferencedAssemblies.Add(typeof(PMR.Stubs.CalculateRequest)); engine.ReferencedAssemblies.Add(typeof(IMessage)); var results = engine.Compile(SelectedCodeTab.Code, Path.GetDirectoryName(SelectedCodeTab.File)).Result; if (results.Count == 0) { SelectedCodeTab.Errors = new ObservableCollection(); Status = "Compiled successfully."; } else { SelectedCodeTab.Errors = results.ToObservableCollection(); Status = results.Count + " compilation errors found!"; } } catch (Exception ex) { Status = "Error compiling!"; SelectedCodeTab.Errors = new ObservableCollection() { new CompilerError() { Error = ex.Message } }; } }); } /// /// Stops the currently current script. /// private void StopTab() { if (_stubManager != null) { _stubManager.Abort(); IsRunning = false; SelectedCodeTab.IsRunning = false; Status = "Stopped!"; AppendTextLog((DateTime.Now.ToTimeString() + ": ") + "Stopped!" + Environment.NewLine); } } #endregion #region Public Methods public void SetLogTextBox(TextBox logTextBox) { _logTextBox = logTextBox; } #endregion #region Event Handlers /// /// Handled the Executed event. /// /// The sender. /// Name of the stub. private void Manager_Executed(object sender, string stubName) { if (AppendLogAuto) { AppendTextLog((DateTime.Now.ToTimeString() + ": ") + "Executing '" + stubName + "'..." + Environment.NewLine); } } /// /// Handled the Failed event. /// /// The sender. /// The exception. private void Manager_Failed(object sender, Exception ex) { if (IsRunning) { if (AppendLogAuto) { AppendTextLog((DateTime.Now.ToTimeString() + ": ") + ex.Message + Environment.NewLine); } } } /// /// Handled the Completed event. /// /// The sender. /// The response. private void Manager_Completed(object sender, string response) { if (AppendLogAuto) { AppendTextLog((DateTime.Now.ToTimeString() + ": ") + "Response Received:" + Environment.NewLine); AppendTextLog((DateTime.Now.ToTimeString() + ": ") + response + Environment.NewLine); } } private void AppendTextLog(String log) { _consoleDispatcher.Push(log); } private void ClearTextLog() { LogManager.Log("Log Cleared -----------------------------------------------------------------"); InvokeUI(() => { _logTextBox.Text = String.Empty; }); } #endregion } }