using Hylasoft.Opc.Common; using System; using System.Collections.Generic; using System.Linq; namespace Hylasoft.Opc.Cli { internal class Repl { private readonly IOpcClient _client; private Node _currentNode; private bool _keepAlive = true; public Repl(IOpcClient client) { _client = client; } /// <summary> /// Starts the REPL routing /// </summary> public void Start() { while (_keepAlive) { try { Console.WriteLine(); Console.Write(_currentNode.Tag + ": "); var line = Console.ReadLine(); var command = CreateCommand(line); RunCommand(command); } catch (BadCommandException) { Console.WriteLine("Invalid command or arguments"); Console.WriteLine(); RunCommand(new Command(SupportedCommands.Help)); } catch (Exception e) { Console.WriteLine("An error occurred running the last command:"); Console.WriteLine(e.Message); } } // ReSharper disable once FunctionNeverReturns } private void RunCommand(Command command) { switch (command.Cmd) { case SupportedCommands.Help: ShowHelp(); break; case SupportedCommands.Read: Read(command.Args); break; case SupportedCommands.Write: Write(command.Args); break; case SupportedCommands.Up: _currentNode = _currentNode.Parent; break; case SupportedCommands.Monitor: Monitor(command.Args); break; case SupportedCommands.Exit: _client.Dispose(); _keepAlive = false; break; default: throw new BadCommandException(); } } #region commands private void Write(IList<string> args) { if (args.Count < 2) throw new BadCommandException(); var tag = args[0]; var val = args[1]; var type = _client.GetDataType(GenerateRelativeTag(tag)); switch (type.Name) { case "Int32": var val32 = Convert.ToInt32(val); _client.Write<int>(GenerateRelativeTag(tag), val32); break; case "Int16": var val16 = Convert.ToInt16(val); _client.Write<short>(GenerateRelativeTag(tag), val16); break; case "UInt16": var valuint16 = Convert.ToUInt16(val); _client.Write<ushort>(GenerateRelativeTag(tag), valuint16); break; case "UInt32": var valuint32 = Convert.ToUInt32(val); _client.Write<uint>(GenerateRelativeTag(tag), valuint32); break; case "Boolean": var valBool = Convert.ToBoolean(val); _client.Write<bool>(GenerateRelativeTag(tag), valBool); break; case "Int64": var val64 = Convert.ToInt64(val); _client.Write<long>(GenerateRelativeTag(tag), val64); break; case "UInt64": var valuint64 = Convert.ToUInt64(val); _client.Write<ulong>(GenerateRelativeTag(tag), valuint64); break; default: _client.Write<object>(GenerateRelativeTag(tag), val); break; } } private void Monitor(IList<string> args) { if (!args.Any()) throw new BadCommandException(); var stopped = false; _client.Monitor<object>(GenerateRelativeTag(args[0]), (o, stop) => { // ReSharper disable once AccessToModifiedClosure if (stopped) stop(); else Console.WriteLine("Value changed: " + o); }); Console.WriteLine("Started monitoring. Press any key to interrupt."); Console.ReadKey(true); stopped = true; } private void Read(IList<string> args) { if (!args.Any()) throw new BadCommandException(); var value = _client.Read<object>(GenerateRelativeTag(args[0])); Console.WriteLine(value); } private static void ShowHelp() { Console.WriteLine("Supported commands:"); Console.WriteLine(" ls: Display the subnodes"); Console.WriteLine(" cd [tag]: Visit a children node"); Console.WriteLine(" read [tag]: Read the node"); Console.WriteLine(" write [tag] [value]: Write value on node"); Console.WriteLine(" root: Go to root node"); Console.WriteLine(" up: Go up one folder"); Console.WriteLine(" monitor [node]: monitor the node"); Console.WriteLine("subnodes are separated by '.' The tag is relative to the current folder"); } #endregion private static Command CreateCommand(string line) { try { var cmd = CliUtils.SplitArguments(line); var args = cmd.Skip(1).ToList(); SupportedCommands selectedCommand; if (!Enum.TryParse(cmd[0], true, out selectedCommand)) selectedCommand = SupportedCommands.Help; return new Command(selectedCommand, args); } catch (Exception e) { throw new BadCommandException(e.Message, e); } } private string GenerateRelativeTag(string relativeTag) { return relativeTag; } } }