virtual protected void DisconnectADM(String port, String nodeID = null)
        {
            if (PortSharing)
            {
                //when using a shared port we connect all the boards in one go
                var ar = nodeID == null?RequiredBoards.Split(',') : new String[]
                {
                    nodeID
                };
                foreach (String nid in ar)
                {
                    String key = port + ":" + nid;
                    if (!ADMS.ContainsKey(key))
                    {
                        continue;
                    }

                    Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::DisconnectADM: Attempting to disconnect board @ {0}", key);
                    ArduinoDeviceManager adm = ADMS[key];
                    if (adm != null)
                    {
                        adm.Disconnect();
                    }
                    ADMS.Remove(key);
                    _devicesConnected.Remove(key);
                    Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::DisconnectADM: Disconnected board @ {0}", key);
                }
            }
            else
            {
                if (!ADMS.ContainsKey(port))
                {
                    return;
                }

                ArduinoDeviceManager adm = ADMS[port];
                if (adm != null)
                {
                    adm.Disconnect();
                }
                ADMS.Remove(port);
                _devicesConnected.Remove(port);

                Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::DisconnectADM: Board {0} on port {1} disconnected", adm.BoardID, port);
                Broadcast(ADMEvent.DISCONNECTED, String.Format("{0} disconnected from port {1}", adm.BoardID, port));
            }
        }
        virtual protected void ConnectADM(String port, String nodeID = null)
        {
            //turn off the sampler ... it will start again when all boards have been connected
            Sampler.Stop();

            if (PortSharing)
            {
                if (RequiredBoards == null)
                {
                    throw new Exception("If using a shared port, 'Nodes' on the port must be specified as the 'RequiredBoards' property");
                }

                //when using a shared port we connect all the boards in one go
                var boards = nodeID == null?RequiredBoards.Split(',') : new String[]
                {
                    nodeID
                };
                List <Exception> exs = new List <Exception>();

                foreach (String nid in boards)
                {
                    String key = port + ":" + nid;
                    if (ADMS.ContainsKey(key))
                    {
                        continue;
                    }

                    try
                    {
                        //now proceed to connect
                        ADMS[key] = null; //reserve a place (cos the connection process takes a while)
                        Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::ConnectADM: Attempting to connect board @ {0}", key);
                        ArduinoDeviceManager adm = ArduinoDeviceManager.Connect(nid, port, BaudRate, TryHandleADMMessage);
                        adm.Sampler            = Sampler;
                        adm.Tracing            = Tracing;
                        ADMS[key]              = adm;
                        _devicesConnected[key] = false;
                        Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::ConnectADM: Connected to board @ {0}", key);
                        Broadcast(ADMEvent.CONNECTED, String.Format("ADM now Connected @ {0}", key));

                        //Wait here (i.e. before connecting to another board) until the adm is of status device connected
                        while (!adm.DevicesConnected)
                        {
                            System.Threading.Thread.Sleep(1000);
                        }
                    } catch (Exception e)
                    {
                        ADMS.Remove(key);

                        //TODO: remove the trace and create a single 'aggregate' exception and throw that after th eloop
                        Tracing?.TraceEvent(TraceEventType.Error, 100, "ADMService::ConnectADM: Error connection to board @ {0}: {1}", key, e.Message);
                        exs.Add(e);
                    }
                } //end loop of all required boards
            }
            else
            {
                String key = port;
                if (ADMS.ContainsKey(key))
                {
                    throw new Exception("ADMService::ConnectADM: port " + port + " already has an assigned ADM");
                }

                Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::ConnectADM: Attempting to connect board on {0} port {0}", port);
                try
                {
                    ADMS[key] = null; //reserve a place (cos the connection process takes a while)

                    //now connct
                    ArduinoDeviceManager adm = ArduinoDeviceManager.Connect(port, BaudRate, TryHandleADMMessage);
                    adm.Sampler            = Sampler;
                    adm.Tracing            = Tracing;
                    ADMS[key]              = adm;
                    _devicesConnected[key] = false;
                    Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::ConnectADM: Connected to board on port {0}", port);
                    Broadcast(ADMEvent.CONNECTED, String.Format("Connected ADM to port {0}", port));
                } catch (Exception e) {
                    ADMS.Remove(key);

                    //TODO: remove the trace and create a single 'aggregate' exception and throw that after th eloop
                    Tracing?.TraceEvent(TraceEventType.Error, 100, "ADMService::ConnectADM: Error connection to board @ {0}: {1}", key, e.Message);
                }
            }
        }
        override public bool HandleCommand(Connection cnn, Message message, String cmd, List <Object> args, Message response)
        {
            bool                 respond = true;
            MessageSchema        schema  = new ADMService.MessageSchema(response);
            DeviceManager        devMgr;
            ArduinoDeviceManager adm;

            switch (cmd)
            {
            case "status":
                schema.AddADMS(ADMS);
                List <String> ports = ArduinoDeviceManager.GetBoardPorts(SupportedBoards, AllowedPorts, DeniedPorts);
                schema.AddPorts(ports);
                schema.AddRequiredBoards(RequiredBoards);

                devMgr = DeviceManager.GetInstance();
                List <String> portDevices = new List <String>();
                foreach (String port in ports)
                {
                    List <DeviceManager.DeviceInfo> devs = devMgr.GetDevices("(" + port + ")");
                    foreach (DeviceManager.DeviceInfo devInfo in devs)
                    {
                        String s = String.Format("{0} ({1}): {2}", devInfo.Description, devInfo.InstanceID, devInfo.Status);
                        portDevices.Add(s);
                    }
                }
                response.AddValue("PortDevices", portDevices);
                break;

            case "disable-port":
            case "enable-port":
            case "reset-port":
                if (args.Count != 1 || args[0] == null || args[0].ToString() == String.Empty)
                {
                    throw new Exception("No port specified");
                }
                String devicePort = args[0].ToString().ToUpper();
                devMgr = DeviceManager.GetInstance();
                List <DeviceManager.DeviceInfo> ar = devMgr.GetDevices("(" + devicePort + ")");
                if (ar.Count != 1)
                {
                    throw new Exception("Cannot find device on port " + devicePort);
                }
                DeviceManager.DeviceInfo di = ar[0];

                //first we check if there is an ADM on this port
                String output = "";
                lock (_lockMonitorADM)
                {
                    if (ADMS.ContainsKey(devicePort))
                    {
                        output += String.Format("Found ADM on port {0} so disconnecting first", devicePort) + Environment.NewLine;
                        DisconnectADM(devicePort);
                        System.Threading.Thread.Sleep(500);
                        if (SerialPorts.IsOpen(devicePort))
                        {
                            throw new Exception("Serial port is open on " + devicePort);
                        }
                    }

                    Process proc = null;
                    if (cmd == "disable-port")
                    {
                        proc = devMgr.DisableDevice(di.InstanceID);
                    }
                    else if (cmd == "enable-port")
                    {
                        proc = devMgr.EnableDevice(di.InstanceID);
                    }
                    else
                    {
                        proc = devMgr.ResetDevice(di.InstanceID);
                    }
                    output += proc.StandardOutput.ReadToEnd();
                }
                response.Value = output;
                break;

            default:
                var tgtcmd = cmd.Split(':');
                if (tgtcmd.Length < 2)
                {
                    throw new Exception(String.Format("ADM: Unrecognised command {0}", cmd));
                }

                //Check that there are any boards connected
                if (ADMS.Count == 0)
                {
                    throw new Exception("ADM: No boards connected");
                }

                //so this is an ADM command, find the board first
                adm = GetADM(tgtcmd[0]);

                if (adm == null)
                {
                    throw new Exception(String.Format("ADM: Cannot find ADM {0}", tgtcmd[0]));
                }

                if (!adm.IsConnected)
                {
                    throw new Exception(String.Format("ADM: {0} is not conntected", adm.BoardID));
                }

                //handle commands related to the board (i.e. not to a specific added device)
                if (tgtcmd.Length == 2)
                {
                    int repeat;    //frequently used var name
                    int delay;     //frequently used var name
                    switch (tgtcmd[1].ToLower())
                    {
                    case "status":
                        AddADMRequest(adm, adm.RequestStatus(), response.Target);
                        respond = false;
                        break;

                    case "ping":
                        AddADMRequest(adm, adm.Ping(), response.Target);
                        respond = false;
                        break;

                    case "pingloadtest":
                        repeat = args != null && args.Count > 0 ? System.Convert.ToInt16(args[0]) : 10;
                        delay  = args != null && args.Count > 1 ? System.Convert.ToInt16(args[1]) : 500;
                        Task.Run(() =>
                        {
                            for (int i = 0; i < repeat; i++)
                            {
                                adm.Ping();
                                System.Threading.Thread.Sleep(delay);
                            }
                        });
                        respond = false;
                        break;

                    case "capability":
                        var lbc = adm.ListBoardCapability();
                        response.AddValue("PinCount: ", lbc.Count);
                        response.AddValue("Pins", lbc);
                        break;

                    case "disconnect":
                        DisconnectADM(adm.Port);
                        break;

                    case "setdigitalpin":
                        if (args.Count < 2)
                        {
                            throw new Exception("Insufficient arguments ... must supply a pin number and value");
                        }
                        int  pin = System.Convert.ToInt16(args[0]);
                        bool val = Chetch.Utilities.Convert.ToBoolean(args[1]);
                        if (!adm.IsPinCapable(pin, Solid.Arduino.Firmata.PinMode.DigitalOutput))
                        {
                            throw new Exception(String.Format("Pin {0} is not capabale of digital output", pin));
                        }
                        if (adm.GetDevicesByPin(pin) != null)
                        {
                            throw new Exception(String.Format("Pin {0} is being used by a device", pin));
                        }
                        adm.SetDigitalPinMode(pin, Solid.Arduino.Firmata.PinMode.DigitalOutput);
                        adm.SetDigitalPin(pin, val);
                        break;

                    case "setdigitalport":
                        if (args.Count < 2)
                        {
                            throw new Exception("Insufficient arguments ... must supply a port number and value");
                        }
                        int  port   = System.Convert.ToInt16(args[0]);
                        bool enable = Chetch.Utilities.Convert.ToBoolean(args[1]);
                        adm.SetDigitalReportMode(port, enable);
                        //adm.G
                        break;

                    case "list-devices":
                        schema.AddDevices(adm.GetDevices());
                        break;

                    case "list-pins":

                        break;

                    default:
                        throw new Exception(String.Format("No ADM direct command {0}", tgtcmd[1]));
                    }
                }
                else
                {
                    //handle command specific to device
                    try
                    {
                        respond = HandleADMDeviceCommand(adm, tgtcmd[1], tgtcmd[2], args, response);
                    } catch (Exception e)
                    {
                        Tracing?.TraceEvent(TraceEventType.Error, 0, "Exception: {0}", e.Message);
                        throw e;
                    }
                }

                if (response.Value == null || response.Value == String.Empty)
                {
                    response.Value = "Handled " + cmd;
                }

                break;
            }

            return(respond);
        }