static public ArduinoDeviceManager Connect(String nodeID, String port, SerialBaudRate bps, int timeOut, Action <ADMMessage, ArduinoDeviceManager> listener) { if (String.IsNullOrEmpty(nodeID)) { throw new Exception("ArduinoDeviceManager::Connect ... nodeID cannot be empty or null"); } ISerialConnection connection = new XBeeFirmataSerialConnection(nodeID, port, bps); if (connection != null) { var session = new ArduinoSession(connection, timeOut); try { var mgr = new ArduinoDeviceManager(session, listener, port, nodeID); mgr.Connection = connection; return(mgr); } catch (Exception e) { if (connection.IsOpen) { connection.Close(); } throw e; } } return(null); }
virtual protected void OnADMDevicesConnected(ArduinoDeviceManager adm, ADMMessage message) { _devicesConnected[adm.PortAndNodeID] = true; bool startSampler = false; if (String.IsNullOrEmpty(RequiredBoards)) { startSampler = true; } else { String[] ar = RequiredBoards.Split(','); startSampler = true; for (int i = 0; i < ar.Length; i++) { ArduinoDeviceManager amg = GetADM(ar[i]); if (amg == null || amg.State != ADMState.DEVICE_CONNECTED) { startSampler = false; break; } } } if (startSampler) { Sampler.Start(); Tracing?.TraceEvent(TraceEventType.Information, 0, "Started sampler with {0} subjects and tick time of {1}", Sampler.SubjectCount, Sampler.TimerInterval); } String msg = String.Format("All {0} added devices connected for {1} ", adm.DeviceCount, adm.BoardID); Broadcast(ADMEvent.DEVICES_CONNECTED, msg); }
public ADMRequest(ArduinoDeviceManager adm, byte tag, String target, int ttl = 60 * 1000) { ADM = adm; Tag = tag; Target = target; Requested = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; _ttl = ttl; }
protected void AddADMRequest(ArduinoDeviceManager adm, byte tag, String replyTo) { if (tag == 0) { throw new ArgumentException("AddADMRequest: Tag cannot be 0"); } //we intentionally allow for the possibility of this being overwritten by a future request _admRequests.Add(new ADMRequest(adm, tag, replyTo)); }
virtual protected bool HandleADMDeviceCommand(ArduinoDeviceManager adm, String deviceID, String command, List <Object> args, Message response) { if (adm == null) { throw new Exception("No ADM provided"); } if (!adm.HasDevice(deviceID)) { throw new Exception(String.Format("Device {0} has not been added to ADM", deviceID)); } bool respond = true; ArduinoDevice device = null; MessageSchema schema = new ADMService.MessageSchema(response); switch (command) { case "list-commands": device = adm.GetDevice(deviceID); schema.AddDeviceCommands(device); break; case "status": device = adm.GetDevice(deviceID); if (device.BoardID == 0) { throw new Exception(String.Format("Device {0} does not have a board ID", deviceID)); } AddADMRequest(adm, adm.RequestStatus(device.BoardID), response.Target); respond = false; break; default: var commands = command.Split(','); foreach (var cmd in commands) { var tcmd = cmd.Trim(); if (tcmd.ToLower().IndexOf("wait") == 0) { int delay = tcmd.Length > 4 ? System.Convert.ToInt16(tcmd.Substring(4, tcmd.Length - 4)) : 200; System.Threading.Thread.Sleep(delay); } else { byte tag = adm.IssueCommand(deviceID, tcmd, args); if (tag > 0) { AddADMRequest(adm, tag, response.Target); respond = false; } } } break; } return(respond); }
private void TryHandleADMMessage(ADMMessage message, ArduinoDeviceManager adm) { try { HandleADMMessage(message, adm); } catch (Exception e) { Tracing?.TraceEvent(TraceEventType.Error, 100, "HandleADMMessage Error handling {0}: {1}", message.Type, e.Message); } }
protected virtual bool OnADMInactivityTimeout(ArduinoDeviceManager adm, long msQuiet) { Tracing?.TraceEvent(TraceEventType.Warning, 100, "ADMService::MonitorADM: Last activity of ADM (BoardID={0}) on {1} was {2} ms ago ... so attempting a clear", adm.BoardID, adm.PortAndNodeID, msQuiet); try { adm.Clear(); Tracing?.TraceEvent(TraceEventType.Information, 100, "ADMService::MonitorADM: Clearing ADM (BoardID={0}) on {1} was successful so attempting to Ping", adm.BoardID, adm.PortAndNodeID); adm.RequestStatus(); return(true); } catch (Exception e) { Tracing?.TraceEvent(TraceEventType.Error, 100, "ADMService::MonitorADM: Error clearing ADM (BoardID={0}) on {1}: {2} {3}", adm.BoardID, adm.PortAndNodeID, e.GetType(), e.Message); return(false); } }
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)); } }
public void PrepareForBroadcast(ArduinoDeviceManager adm) { ADMMessage message = (ADMMessage)Message; message.AddValue("BoardID", adm.BoardID); ArduinoDevice dev = null; if (message.TargetID > 0) { dev = adm.GetDeviceByBoardID(message.TargetID); } else if (message.Sender != null && message.Sender != String.Empty) { dev = adm.GetDevice(message.Sender); } message.AddValue(DEVICE_ID, dev != null ? dev.ID : ""); message.AddValue(DEVICE_NAME, dev != null ? dev.Name : ""); }
protected ADMRequest GetADMRequest(ArduinoDeviceManager adm, byte tag, bool remove = true) { ADMRequest req2return = null; foreach (ADMRequest req in _admRequests) { if (req.ADM == adm && req.Tag == tag) { req2return = req; break; } } if (req2return != null) { _admRequests.Remove(req2return); } return(req2return); }
static public ArduinoDeviceManager Connect(String port, SerialBaudRate bps, int timeOut, Action <ADMMessage, ArduinoDeviceManager> listener) { ISerialConnection connection = new EnhancedSerialConnection(port, bps); if (connection != null) { var session = new ArduinoSession(connection, timeOut); try { var mgr = new ArduinoDeviceManager(session, listener, port); return(mgr); } catch (Exception e) { if (connection.IsOpen) { connection.Close(); } throw e; } } return(null); }
abstract protected void AddADMDevices(ArduinoDeviceManager adm, ADMMessage message);
/// <summary> /// Called by a timer /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> protected virtual void MonitorADM(Object sender, ElapsedEventArgs eventArgs) { _admtimer.Stop(); try { lock (_lockMonitorADM) { //get all current ports that have boards connected List <String> ports = ArduinoDeviceManager.GetBoardPorts(SupportedBoards, AllowedPorts, DeniedPorts); //build a list of any ADMs that are no longer connected to one of these ports (e.g. USB has been yanked out) List <ArduinoDeviceManager> adms = ADMS.Values.ToList(); foreach (ArduinoDeviceManager adm in adms) { if (adm == null) { continue; } if (!ports.Contains(adm.Port)) { //if for some reason (config change?) the port for the ADM is not a possible board port DisconnectADM(adm.Port); } } //now we try and connect all the boards that we have not just disconnected but have not yet been connected if (ports.Count > 0 && ADMS.Count < RequiredBoardsCount) { _noPortsFoundWarning = false; foreach (String port in ports) { try { ConnectADM(port); } catch (System.IO.IOException e) { switch (e.HResult) { case Win32.ERROR_ATTACHED_DEVICE_NOT_FUNCTIONING: ResetPort(port, e); break; } Tracing?.TraceEvent(TraceEventType.Error, 100, "ADMService::MonitorADM: Connecting ADM to port {0} produced IOException {1} ({2}): {3}", port, e.GetType().ToString(), e.HResult, e.Message); } catch (Exception e) { Tracing?.TraceEvent(TraceEventType.Error, 100, "ADMService::MonitorADM: Connecting ADM to port {0} produced Exception {1} ({2}): {3}", port, e.GetType().ToString(), e.HResult, e.Message); } } } else if (ADMS.Count < RequiredBoardsCount) { if (!_noPortsFoundWarning) { Tracing?.TraceEvent(TraceEventType.Warning, 100, "ADMService::MonitorADM: No boards connected to any port"); _noPortsFoundWarning = true; } } //Finally we check on the state of the ADM foreach (ArduinoDeviceManager adm in adms) { if (adm == null) { continue; } if (!adm.IsConnected) { //TODO: how do we handle this? Tracing?.TraceEvent(TraceEventType.Warning, 100, "ADM (BDID={0}) @ {1} is of state", adm.State); } else if (ADMInactivityTimeout > 0) { //if there has been a suspicioius lack of activity... long msQuiet = (DateTime.Now.Ticks - adm.LastMessageReceivedOn.Ticks) / TimeSpan.TicksPerMillisecond; if (msQuiet > ADMInactivityTimeout) { OnADMInactivityTimeout(adm, msQuiet); } } } } //end of monitor lock } catch (Exception e) { Tracing?.TraceEvent(TraceEventType.Error, 100, "Unknown exception in MonitorADM: {0}, {1}", e.GetType().ToString(), e.Message); } _admtimer.Start(); }
virtual protected void ReconnectADM(ArduinoDeviceManager adm) { DisconnectADM(adm.Port, adm.NodeID); System.Threading.Thread.Sleep(1000); ConnectADM(adm.Port, adm.NodeID); }
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); }
virtual protected void HandleADMMessage(ADMMessage message, ArduinoDeviceManager adm) { switch (message.Type) { case MessageType.ERROR: ErrorCode errCode = message.HasValue("ErrorCode") ? message.GetEnum <ErrorCode>("ErrorCode") : ErrorCode.ERROR_UNKNOWN; Tracing?.TraceEvent(TraceEventType.Error, 100, "ADM {0} produced error: {1}", adm.BoardID, errCode); switch (errCode) { case ErrorCode.ERROR_ADM_NOT_INITIALISED: ReconnectADM(adm); break; default: //currently do nothing break; } break; case MessageType.WARNING: Tracing?.TraceEvent(TraceEventType.Warning, 100, "ADM {0} produced warning: {1}", adm.BoardID, message.Value); break; case MessageType.STATUS_RESPONSE: if (!PortSharing && !IsRequiredBoard(adm.BoardID)) { Tracing?.TraceEvent(TraceEventType.Warning, 100, "ADM {0} is not one of the required boards ({1}). Disconnecting from port {2}...", adm.BoardID, RequiredBoards, adm.Port); DisconnectADM(adm.Port); break; } if (adm.State == ADMState.DEVICE_READY) { Tracing?.TraceEvent(TraceEventType.Verbose, 100, "ADM: Ready to add devices to {0} on port {1} ...", adm.BoardID, adm.PortAndNodeID); AddADMDevices(adm, message); Tracing?.TraceEvent(TraceEventType.Verbose, 100, "ADM: {0} devices added to {1} on port {2}. Now configure board", adm.DeviceCount, adm.BoardID, adm.PortAndNodeID); adm.Configure(); } else if (adm.State == ADMState.DEVICE_CONNECTED) { if (message.HasValue("Initialised") && !message.GetBool("Initialised")) { //Console.WriteLine("Whoa ... {0} has reset without us knowing", adm.PortAndNodeID); } } break; case MessageType.CONFIGURE_RESPONSE: String key = adm.PortAndNodeID; if (adm.State == ADMState.DEVICE_CONNECTED && !_devicesConnected[key]) { Tracing?.TraceEvent(TraceEventType.Verbose, 100, "ADM: All {0} devices now configured and connected to board {1} on port {2}", adm.DeviceCount, adm.BoardID, adm.PortAndNodeID); OnADMDevicesConnected(adm, message); } break; case MessageType.PING_RESPONSE: break; } if (message.Tag > 0) { ADMRequest req = GetADMRequest(adm, message.Tag); if (req != null) { if (req.HasExpired()) { Tracing?.TraceEvent(TraceEventType.Warning, 0, "ADM request for tag {0} and target {1} has expired so not returning message of type {2}", message.Tag, req.Target, message.Type); return; } else { message.Target = req.Target; } } } var schema = new ADMService.MessageSchema(message); schema.PrepareForBroadcast(adm); //notify other clients listening to this client Broadcast(ADMEvent.MESSAGE, message); }