/// <summary> /// Create instance of EE31 protocol class. /// </summary> /// <param name="communicationInterface">The communication layer</param> /// <returns></returns> public static IEECommProtocol GetEE31Protocol(IEECommDevice communicationInterface) { if (communicationInterface == null) { throw new ArgumentNullException("communicationInterface"); } // Only create one protocol instance per interface lock (_protectInstances) { IEECommProtocol protItf = _protInstances.Find(p => (p.CommunicationInterface.InterfaceType == communicationInterface.InterfaceType && p.CommunicationInterface.InterfaceId == communicationInterface.InterfaceId)); if (protItf != null) { return(protItf); } var prot = new EE31Protocol(communicationInterface); _protInstances.Add(prot); return(prot); } }
private void FireDeviceFound(IEECommDevice device) { if (DeviceFound != null) { if (DeviceFound.Target is ISynchronizeInvoke sync && sync.InvokeRequired) { sync.Invoke(DeviceFound, new object[] { this, device }); } else { DeviceFound(this, device); } }
/// <summary> /// Constructor. The protocol must have an underlying communication layer. /// </summary> /// <param name="communicationInterface">The communication layer</param> internal EE31Protocol(IEECommDevice communicationInterface) { CommunicationInterface = communicationInterface; // Default protocol configuration CmdTimeoutAutoRetries = 1; CmdTimeoutMSec = 2000; IncludeNetworkAddress = true; MaxPayload = 255; SIUS = SIUSUnit.SI; CfgMVCodeTranslations = null; CmdSpecialTreatments = new Dictionary <byte, CmdSpecialTreatment>(); UseMVCodeInsteadOfEE31Index = true; }
public IEECommDevice TryGetDevice(IScanConfiguration config, ushort busAddr) { if (config is ScanConfigurationUART scanConfigurationUART) { if (busAddr == 0) { throw new ArgumentException("Device bus address must not be 0", "busAddr"); } IEECommDevice device = null; // Search in already existing devices before starting a new scan lock (_foundDevices) { device = _foundDevices.FirstOrDefault(x => x.InterfaceType == InterfaceType.UART && x.InterfaceId == busAddr.ToString()); } if (device != null) { // Try to revitalize device IEECommProtocol protEE31 = EECommProtocolFactory.GetEE31Protocol(device); IEECommandResult result = null; // Force silence Thread.Sleep(50); // For non-EE31 devices (Modbus, etc.): Send protocol switch command to make them speak EE31 (again) ushort discoveryId = DiscoveryCmdParams.BuildNewDiscoveryId(); try { result = protEE31.ExecuteCommand(EE31Command.Discovery, new DiscoveryCmdParams(discoveryId, busAddr, 0, 100)); } catch { } try { result = protEE31.ExecuteCommand(EE31Command.Discovery, new DiscoveryCmdParams(discoveryId, busAddr, 0, 50)); } catch { } if (result != null && result.Code == EECmdResultCode.Success) { return(device); } } return(device); } else if (config is ScanConfigurationEmulation scanConfigurationEmulation) { IEECommDevice emulatedDevice = new EECommDeviceEmulation(0, scanConfigurationEmulation.EmulationDevicesSettings.ElementAt(0)); var existing = _foundDevices.FirstOrDefault(x => x.InterfaceType == InterfaceType.Emulation && x.InterfaceId == emulatedDevice.InterfaceId); if (existing == null) { existing = emulatedDevice; } return(existing); } throw new NotImplementedException("Currently it is only possible to get UART or emulated devices."); }
/// <summary> /// Sends a command to the device, expects multiple responses (i.e. broadcast message). /// </summary> /// <param name="commInterface">The communication interface</param> /// <param name="cmd">The command.</param> /// <param name="param">The command parameters.</param> /// <param name="cmdTimeoutRetries">Number of automatic retries in case of timeout.</param> /// <param name="cmdTimeoutMs">Command timeout in milliseconds.</param> /// <param name="busAddr">The target bus addr.</param> /// <param name="multiResponsesExpected">The multi responses expected.</param> /// <param name="responses">The responses.</param> /// <param name="rawDataToTx">Raw data bytes to transmit immediately after sending the command frame.</param> private void SendCmdMultiResponse(IEECommDevice commInterface, byte cmd, byte[] param, uint cmdTimeoutRetries, uint cmdTimeoutMs, int busAddr, bool multiResponsesExpected, out IList <EE31CmdResponseData> responses, byte[] rawDataToTx = null) { lock (__lock_SendCmdMultiResponse) { responses = new List <EE31CmdResponseData>(); commInterface.EnsureConnection(); IList <byte> txBuffer = new List <byte>(); if (IncludeNetworkAddress || 0 != busAddr) { // 16 bit network address UInt16 addr = (UInt16)busAddr; byte[] flatBytes = BitConverter.GetBytes(addr); if (2 != flatBytes.Length) { throw new ArgumentException("UInt16 must have 2 bytes!"); } txBuffer.Add(flatBytes[0]); txBuffer.Add(flatBytes[1]); } // Command byte txBuffer.Add(cmd); // Byte count of parameters if (null != param) { txBuffer.Add((byte)(param.Length)); } else { txBuffer.Add(0); } if (null != param) { // Parameter bytes foreach (byte data in param) { txBuffer.Add(data); } } // Checksum: (BusAddr + Cmd + Param Length + Data 1..Data n) MOD 0x100 byte checksum = 0; foreach (byte by in txBuffer) { checksum += by; } txBuffer.Add(checksum); // --- Send command --- string bufAsHexChars; while (true) { while (DateTime.UtcNow.Subtract(timestampLastTx).TotalMilliseconds < 50) { // Force silence Diagnostic.Msg(2, "SendCmdMultiResponse", "Forcing silence before sending cmd..."); Thread.Sleep(20); } commInterface.WriteBytes(txBuffer.ToArray(), 0, txBuffer.Count); if (null != rawDataToTx && 0 != rawDataToTx.Length) { // Send raw data bytes to transmit immediately after sending the command frame commInterface.WriteBytes(rawDataToTx, 0, rawDataToTx.Length); } // --- Ensure tx buffer is empty (all sent) --- DateTime timeout = DateTime.UtcNow.AddMilliseconds(1000); while (DateTime.UtcNow <= timeout && commInterface.BytesToWrite > 0) { Thread.Sleep(5); } timestampLastTx = DateTime.UtcNow; // -------------------- DIAGNOSTICS -------------------- if (Diagnostic.OutputLevel > 2) { bufAsHexChars = ""; foreach (byte by in txBuffer) { bufAsHexChars += string.Format("{0:X2} ", by); } Diagnostic.Msg(3, "SendCmdMultiResponse", "Cmd " + string.Format("{0:X2}", cmd) + " frame sent: " + bufAsHexChars); } // -------------------- DIAGNOSTICS -------------------- byte[] response; int rxBusAddr; byte[] foundFrame; Queue <byte> rxQueue = new Queue <byte>(); // Timeout in x msecs timeout = DateTime.UtcNow.AddMilliseconds(cmdTimeoutMs); DateTime lastRxByte = DateTime.UtcNow; bool checkOnceAfterByteWasReceivedAndSilencePassed = false; while (DateTime.UtcNow <= timeout) { try { if (commInterface.BytesToRead > 0 || ((DateTime.UtcNow - lastRxByte).TotalMilliseconds > 250 && checkOnceAfterByteWasReceivedAndSilencePassed)) { bool useSlidingWindow = false; if (commInterface.BytesToRead > 0) { byte rxByte = commInterface.ReadByte(); rxQueue.Enqueue(rxByte); lastRxByte = DateTime.UtcNow; checkOnceAfterByteWasReceivedAndSilencePassed = true; // -------------------- DIAGNOSTICS -------------------- if (Diagnostic.OutputLevel > 7) { bufAsHexChars = ""; foreach (byte by in rxQueue) { bufAsHexChars += string.Format("{0:X2} ", by); } Diagnostic.Msg(8, "SendCmdMultiResponse", "RxByte: " + string.Format("{0:X2}", rxByte) + " => " + bufAsHexChars); } // -------------------- DIAGNOSTICS -------------------- } else { // This is the one pass after some period of silence after the // last received byte checkOnceAfterByteWasReceivedAndSilencePassed = false; Diagnostic.Msg(1, "SendCmdMultiResponse", "SendCmdMultiResponse silence passed, try to find packet within possible garbage..."); // Try to find packet within rxQueue using sliding window useSlidingWindow = true; } EE31Result result = TryDecodeEE31Frame(rxQueue.ToArray(), new byte[] { cmd }, true, useSlidingWindow, out byte rxCmd, out response, out rxBusAddr, out foundFrame); if (EE31Result.Invalid != result) { // Valid response, add to result/responses/rxBusAddrs lists EE31CmdResponseData responseData = new EE31CmdResponseData() { Result = result, Response = response, RxBusAddr = rxBusAddr, FoundFrame = foundFrame }; responses.Add(responseData); rxQueue.Clear(); if (!multiResponsesExpected) { // finished! Diagnostic.Msg(2, "SendCmdMultiResponse", "TryDecodeEE31Frame was successful, finished!"); return; } Diagnostic.Msg(2, "SendCmdMultiResponse", "TryDecodeEE31Frame was successful, waiting for more frames..."); } // Receive next byte (if any) continue; } } catch (Exception ex) { Diagnostic.Msg(1, "SendCmdMultiResponse", "Exception: " + ex.Message); } Thread.Sleep(5); } // Timeout period has passed; dump bytes in rxBuffer so far... bufAsHexChars = ""; foreach (byte by in rxQueue) { bufAsHexChars += string.Format("{0:X2} ", by); } Diagnostic.Msg(1, "SendCmdMultiResponse", "ReceiveCmd Timeout, rxBuffer contents: " + bufAsHexChars); // ... and send command again if nothing received and any cmdTimeoutRetries left if (responses.Count > 0 || cmdTimeoutRetries <= 0) { break; } cmdTimeoutRetries--; } if (0 == responses.Count) { if (0 == cmdTimeoutMs) { // No responses expected, so it is ok to have none... Diagnostic.Msg(2, "SendCmdMultiResponse", "No responses expected, leaving immediately..."); return; } // There should have been responses, throw Timeout exception... Diagnostic.Msg(2, "SendCmdMultiResponse", string.Format("No responses for cmd 0x{0:X2} received, throwing TimeoutException!", cmd)); throw new TimeoutException( string.Format("EE31 protocol timeout: No response for cmd 0x{0:X2} received!", cmd)); } Diagnostic.Msg(2, "SendCmdMultiResponse", string.Format("{1} responses for cmd 0x{0:X2} successfully received.", cmd, responses.Count)); } }