private void ReaderThreadLoop() { while (true) { try { byte[] readData = x10interface.ReadData(); if (readData.Length > 0) { DebugLog("X10 >", Utility.ByteArrayToString(readData)); var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { DebugLog( "X10 >", "COMMAND TIMEOUT" ); communicationState = X10CommState.Ready; } // if (communicationState == X10CommState.WaitingAck && readData[0] == (int)X10CommandType.PLC_Ready && readData.Length <= 2) // ack received { DebugLog( "X10 >", "COMMAND SUCCESSFUL" ); communicationState = X10CommState.Ready; } else if ((readData.Length >= 13 || (readData.Length == 2 && readData[0] == 0xFF && readData[1] == 0x00)) && !isInterfaceReady) { UpdateInterfaceTime(false); isInterfaceReady = true; communicationState = X10CommState.Ready; } else if (readData.Length == 2 && communicationState == X10CommState.WaitingChecksum && readData[0] == expectedChecksum && readData[1] == 0x00) { // checksum is received only from CM11 DebugLog( "X10 >", "CKSUM: " + "Expected [" + Utility.ByteArrayToString(new byte[] { expectedChecksum }) + "] Checksum ==> " + Utility.ByteArrayToString(readData) ); //TODO: checksum verification not handled, we just reply 0x00 (OK) SendMessage(new byte[] { 0x00 }); communicationState = X10CommState.WaitingAck; } else if (readData[0] == (int)X10CommandType.Macro) { lastReceivedTs = DateTime.Now; DebugLog("X10 >", "MACRO: " + Utility.ByteArrayToString(readData)); } else if (readData[0] == (int)X10CommandType.RF) { lastReceivedTs = DateTime.Now; DebugLog("X10 >", "RFCOM: " + Utility.ByteArrayToString(readData)); if (RfDataReceived != null) { Thread signal = new Thread(() => { addressedModules.Clear(); RfDataReceived(new RfDataReceivedAction() { RawData = readData }); }); signal.Start(); } } else if ((readData[0] == (int)X10CommandType.PLC_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (byte)X10CommandType.PLC_ReplyToPoll }); // reply to poll } else if ((readData[0] == (int)X10CommandType.PLC_FilterFail_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (int)X10CommandType.PLC_FilterFail_Poll }); // reply to filter fail poll } else if ((readData[0] == (int)X10CommandType.PLC_Poll)) { lastReceivedTs = DateTime.Now; DebugLog("X10 >", "PLCRX: " + Utility.ByteArrayToString(readData)); // if (readData.Length > 3) { bool newAddressData = true; int messageLength = readData[1]; if (readData.Length > messageLength - 2) { char[] bitmapData = Convert.ToString(readData[2], 2).PadLeft(8, '0').ToCharArray(); byte[] functionBitmap = new byte[messageLength - 1]; for (int i = 0; i < functionBitmap.Length; i++) { functionBitmap[i] = byte.Parse(bitmapData[7 - i].ToString()); } byte[] messageData = new byte[messageLength - 1]; Array.Copy(readData, 3, messageData, 0, messageLength - 1); // CM15 Extended receive has got inverted data if (messageLength > 2 && x10interface.GetType().Equals(typeof(CM15))) { Array.Reverse(functionBitmap, 0, functionBitmap.Length); Array.Reverse(messageData, 0, messageData.Length); } DebugLog("X10 >", "FNMAP: " + Utility.ByteArrayToString(functionBitmap)); DebugLog("X10 >", " DATA: " + Utility.ByteArrayToString(messageData)); for (int b = 0; b < messageData.Length; b++) { // read current byte data (type: 0x00 address, 0x01 function) if (functionBitmap[b] == (byte)X10FunctionType.Address) // address { string housecode = ((X10HouseCodes)Convert.ToInt16( messageData[b].ToString("X2").Substring( 0, 1 ), 16 )).ToString(); string unitcode = ((X10UnitCodes)Convert.ToInt16( messageData[b].ToString("X2").Substring( 1, 1 ), 16 )).ToString(); if (unitcode.IndexOf("_") > 0) unitcode = unitcode.Substring(unitcode.IndexOf("_") + 1); // DebugLog("X10 >", " " + b + ") House code = " + housecode); DebugLog("X10 >", " " + b + ") Unit code = " + unitcode); // string currentUnitCode = housecode + unitcode; if (!moduleStatus.Keys.Contains(currentUnitCode)) { var module = new X10Module() { Code = currentUnitCode }; module.PropertyChanged += ModulePropertyChanged; moduleStatus.Add(currentUnitCode, module); } var mod = moduleStatus[currentUnitCode]; // //TODO: this needs more testing.... if (!addressedModules.ContainsKey(housecode)) { addressedModules.Add(housecode, new List<X10Module>()); } else if (newAddressData) { newAddressData = false; addressedModules[housecode].Clear(); } // if (!addressedModules[housecode].Contains(mod)) { addressedModules[housecode].Add(mod); } } else if (functionBitmap[b] == (byte)X10FunctionType.Function) // function { string currentCommand = ((X10Command)Convert.ToInt16( messageData[b].ToString("X2").Substring( 1, 1 ), 16 )).ToString().ToUpper(); string currentHouseCode = ((X10HouseCodes)Convert.ToInt16( messageData[b].ToString("X2").Substring( 0, 1 ), 16 )).ToString(); // DebugLog("X10 >", " " + b + ") House code = " + currentHouseCode); DebugLog("X10 >", " " + b + ") Command = " + currentCommand); // // //TODO: this needs more testing.... if (!addressedModules.ContainsKey(currentHouseCode)) { addressedModules.Add(currentHouseCode, new List<X10Module>()); } // switch (currentCommand) { case "ALL_UNITS_OFF": if (currentHouseCode != "") AllUnitsOff(currentHouseCode); break; case "ALL_LIGHTS_ON": if (currentHouseCode != "") AllLightsOn(currentHouseCode); break; case "ON": ModulesOn(currentHouseCode); break; case "OFF": ModulesOff(currentHouseCode); break; case "BRIGHT": ModulesBright(currentHouseCode, messageData[++b]); break; case "DIM": ModulesDim(currentHouseCode, messageData[++b]); break; } // newAddressData = true; } } } } } else if ((readData[0] == (int)X10CommandType.PLC_TimeRequest)) // IS THIS A TIME REQUEST? { UpdateInterfaceTime(false); } else { #region This is an hack for detecting disconnection status on Linux platforms if (readData[0] == 0x00) { zeroChecksumCount++; } else { zeroChecksumCount = 0; } // if (zeroChecksumCount > 10) { zeroChecksumCount = 0; gotReadWriteError = true; Close(); } #endregion } } } catch (Exception e) { if (!e.GetType().Equals(typeof(TimeoutException)) && !e.GetType().Equals(typeof(OverflowException))) { DebugLog("X10 !", e.Message); DebugLog("X10 !", e.StackTrace); gotReadWriteError = true; } } } }
private void ReaderTask(CancellationToken readerToken) { while (!readerToken.IsCancellationRequested) { try { byte[] readData = x10interface.ReadData(); if (readData.Length > 0) { logger.Debug(BitConverter.ToString(readData)); // last command ACK timeout var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { logger.Warn("Command acknowledge timeout"); communicationState = X10CommState.Ready; } // last command succesfully sent if (communicationState == X10CommState.WaitingAck && readData[0] == (int)X10CommandType.PLC_Ready && readData.Length <= 2) // ack received { logger.Debug("Command succesfull"); communicationState = X10CommState.Ready; } else if ((readData.Length >= 13 || (readData.Length == 2 && readData[0] == 0xFF && readData[1] == 0x00)) && !isInterfaceReady) { OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(true)); UpdateInterfaceTime(false); communicationState = X10CommState.Ready; } else if (readData.Length == 2 && communicationState == X10CommState.WaitingChecksum && readData[0] == expectedChecksum && readData[1] == 0x00) { // checksum is received only from CM11 logger.Debug("Received checksum {0}, expected {1}", BitConverter.ToString(readData), expectedChecksum.ToString("X2")); //TODO: checksum verification not handled, we just reply 0x00 (OK) SendMessage(new byte[] { 0x00 }); communicationState = X10CommState.WaitingAck; } else if (readData[0] == (int)X10CommandType.Macro) { lastReceivedTs = DateTime.Now; logger.Debug("MACRO: {0}", BitConverter.ToString(readData)); } else if (readData[0] == (int)X10CommandType.RF) { lastReceivedTs = DateTime.Now; bool isSecurityCode = (readData.Length == 8 && readData[1] == (byte)X10Defs.RfSecurityPrefix && ((readData[3] ^ readData[2]) == 0x0F) && ((readData[5] ^ readData[4]) == 0xFF)); bool isCodeValid = isSecurityCode || (readData.Length == 6 && readData[1] == (byte)X10Defs.RfCommandPrefix && ((readData[3] & ~readData[2]) == readData[3] && (readData[5] & ~readData[4]) == readData[5])); // Still unknown meaning of the last byte in security codes if (isSecurityCode && readData[7] == 0x80) readData[7] = 0x00; // Repeated messages check if (isCodeValid) { if (lastRfMessage == BitConverter.ToString(readData) && (lastReceivedTs - lastRfReceivedTs).TotalMilliseconds < minRfRepeatDelayMs) { logger.Warn("Ignoring repeated message within {0}ms", minRfRepeatDelayMs); continue; } lastRfMessage = BitConverter.ToString(readData); lastRfReceivedTs = DateTime.Now; } logger.Debug("RFCOM: {0}", BitConverter.ToString(readData)); OnRfDataReceived(new RfDataReceivedEventArgs(readData)); // Decode received 32 bit message // house code + 4th bit of unit code // unit code (3 bits) + function code if (isSecurityCode) { var securityEvent = X10RfSecurityEvent.NotSet; Enum.TryParse<X10RfSecurityEvent>(readData[4].ToString(), out securityEvent); uint securityAddress = BitConverter.ToUInt32(new byte[] { readData[2], readData[6], readData[7], 0x00 }, 0); if (securityEvent != X10RfSecurityEvent.NotSet) { logger.Debug("Security Event {0} Address {1}", securityEvent, securityAddress); OnRfSecurityReceived(new RfSecurityReceivedEventArgs(securityEvent, securityAddress)); } else { logger.Warn("Could not parse security event"); } } else if (isCodeValid) { // Parse function code var hf = X10RfFunction.NotSet; Enum.TryParse<X10RfFunction>(readData[4].ToString(), out hf); // House code (4bit) + unit code (4bit) byte hu = readData[2]; // Parse house code var houseCode = X10HouseCode.NotSet; Enum.TryParse<X10HouseCode>((Utility.ReverseByte((byte)(hu >> 4)) >> 4).ToString(), out houseCode); switch (hf) { case X10RfFunction.Dim: case X10RfFunction.Bright: logger.Debug("Command {0}", hf); if (hf == X10RfFunction.Dim) CommandEvent_Dim((byte)X10Defs.DimBrightStep); else CommandEvent_Bright((byte)X10Defs.DimBrightStep); OnRfCommandReceived(new RfCommandReceivedEventArgs(hf, X10HouseCode.NotSet, X10UnitCode.Unit_NotSet)); break; case X10RfFunction.AllLightsOn: case X10RfFunction.AllLightsOff: if (houseCode != X10HouseCode.NotSet) { logger.Debug("Command {0} HouseCode {1}", hf, houseCode); if (hf == X10RfFunction.AllLightsOn) CommandEvent_AllLightsOn(houseCode); else CommandEvent_AllUnitsOff(houseCode); OnRfCommandReceived(new RfCommandReceivedEventArgs(hf, houseCode, X10UnitCode.Unit_NotSet)); } else { logger.Warn("Unable to decode house code value"); } break; case X10RfFunction.NotSet: logger.Warn("Unable to decode function value"); break; default: // Parse unit code string houseUnit = Convert.ToString(hu, 2).PadLeft(8, '0'); string unitFunction = Convert.ToString(readData[4], 2).PadLeft(8, '0'); string uc = (Convert.ToInt16(houseUnit.Substring(5, 1) + unitFunction.Substring(1, 1) + unitFunction.Substring(4, 1) + unitFunction.Substring(3, 1), 2) + 1).ToString(); // Parse module function var fn = X10RfFunction.NotSet; Enum.TryParse<X10RfFunction>(unitFunction[2].ToString(), out fn); switch (fn) { case X10RfFunction.On: case X10RfFunction.Off: var unitCode = X10UnitCode.Unit_NotSet; Enum.TryParse<X10UnitCode>("Unit_" + uc.ToString(), out unitCode); if (unitCode != X10UnitCode.Unit_NotSet) { logger.Debug("Command {0} HouseCode {1} UnitCode {2}", fn, houseCode, unitCode.Value()); UnselectModules(); SelectModule(houseCode.ToString() + unitCode.Value().ToString()); if (fn == X10RfFunction.On) CommandEvent_On(); else CommandEvent_Off(); OnRfCommandReceived(new RfCommandReceivedEventArgs(fn, houseCode, unitCode)); } else { logger.Warn("Could not parse unit code"); } break; } break; } } else { logger.Warn("Bad Rf message received"); } } else if ((readData[0] == (int)X10CommandType.PLC_Poll) && readData.Length <= 2) { OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(true)); SendMessage(new byte[] { (byte)X10CommandType.PLC_ReplyToPoll }); // reply to poll } else if ((readData[0] == (int)X10CommandType.PLC_FilterFail_Poll) && readData.Length <= 2) { OnConnectionStatusChanged(new ConnectionStatusChangedEventArgs(true)); SendMessage(new byte[] { (int)X10CommandType.PLC_FilterFail_Poll }); // reply to filter fail poll } else if ((readData[0] == (int)X10CommandType.PLC_Poll)) { lastReceivedTs = DateTime.Now; logger.Debug("PLCRX: {0}", BitConverter.ToString(readData)); if (readData.Length > 3) { int messageLength = readData[1]; if (readData.Length > messageLength - 2) { char[] bitmapData = Convert.ToString(readData[2], 2).PadLeft(8, '0').ToCharArray(); byte[] functionBitmap = new byte[messageLength - 1]; for (int i = 0; i < functionBitmap.Length; i++) { functionBitmap[i] = byte.Parse(bitmapData[7 - i].ToString()); } byte[] messageData = new byte[messageLength - 1]; Array.Copy(readData, 3, messageData, 0, messageLength - 1); // CM15 Extended receive has got inverted data if (messageLength > 2 && x10interface.GetType().Equals(typeof(CM15))) { Array.Reverse(functionBitmap, 0, functionBitmap.Length); Array.Reverse(messageData, 0, messageData.Length); } logger.Debug("FNMAP: {0}", BitConverter.ToString(functionBitmap)); logger.Debug("DATA : {0}", BitConverter.ToString(messageData)); for (int b = 0; b < messageData.Length; b++) { // read current byte data (type: 0x00 address, 0x01 function) if (functionBitmap[b] == (byte)X10FunctionType.Address) // address { X10HouseCode houseCode = (X10HouseCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(0, 1), 16); X10UnitCode unitCode = (X10UnitCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(1, 1), 16); string address = Utility.HouseUnitCodeFromEnum(houseCode, unitCode); logger.Debug(" {0}) Address = {1}", b, address); if (newAddressData) { newAddressData = false; UnselectModules(); } SelectModule(address); OnPlcAddressReceived(new PlcAddressReceivedEventArgs(houseCode, unitCode)); } else if (functionBitmap[b] == (byte)X10FunctionType.Function) // function { var command = (X10Command)Convert.ToInt16(messageData[b].ToString("X2").Substring(1, 1), 16); var houseCode = X10HouseCode.NotSet; Enum.TryParse<X10HouseCode>(Convert.ToInt16(messageData[b].ToString("X2").Substring(0, 1), 16).ToString(), out houseCode); logger.Debug(" {0}) House code = {1}", b, houseCode); logger.Debug(" {0}) Command = {1}", b, command); switch (command) { case X10Command.All_Lights_Off: if (houseCode != X10HouseCode.NotSet) CommandEvent_AllUnitsOff(houseCode); break; case X10Command.All_Lights_On: if (houseCode != X10HouseCode.NotSet) CommandEvent_AllLightsOn(houseCode); break; case X10Command.On: CommandEvent_On(); break; case X10Command.Off: CommandEvent_Off(); break; case X10Command.Bright: CommandEvent_Bright(messageData[++b]); break; case X10Command.Dim: CommandEvent_Dim(messageData[++b]); break; } newAddressData = true; OnPlcFunctionReceived(new PlcFunctionReceivedEventArgs(command, houseCode)); } } } } } else if ((readData[0] == (int)X10CommandType.PLC_TimeRequest)) // IS THIS A TIME REQUEST? { UpdateInterfaceTime(false); } else { #region This is an hack for detecting disconnection status on Linux/Mono platforms if (readData[0] == 0x00) { zeroChecksumCount++; } else { zeroChecksumCount = 0; } // if (zeroChecksumCount > 10) { zeroChecksumCount = 0; gotReadWriteError = true; Close(); } else { SendMessage(new byte[] { 0x00 }); } #endregion } } } catch (Exception e) { if (!e.GetType().Equals(typeof(TimeoutException)) && !e.GetType().Equals(typeof(OverflowException))) { gotReadWriteError = true; logger.Error(e); } } } }
private void SendMessage(byte[] message) { try { // Wait for message delivery acknowledge if (message.Length > 1 && IsConnected) { lock(waitAckMonitor) { while((DateTime.Now - lastReceivedTs).TotalMilliseconds < 500) { Thread.Sleep(50); } DebugLog("X10 <", Utility.ByteArrayToString(message)); x10interface.WriteData(message); commandLastMessage = message; waitAckTimestamp = DateTime.Now; if (x10interface.GetType().Equals(typeof(CM11))) { expectedChecksum = (byte)((message[0] + message[1]) & 0xff); communicationState = X10CommState.WaitingChecksum; } else { communicationState = X10CommState.WaitingAck; } while (commandResendAttempts < commandResendMax && communicationState != X10CommState.Ready) { var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; while (elapsedFromWaitAck.TotalSeconds < commandTimeoutSeconds && communicationState != X10CommState.Ready) { Thread.Sleep(50); elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; } if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { // Resend last message commandResendAttempts++; DebugLog( "X10 >", "PREVIOUS COMMAND TIMED OUT, RESENDING(" + commandResendAttempts + ")" ); x10interface.WriteData(commandLastMessage); waitAckTimestamp = DateTime.Now; } } commandResendAttempts = 0; commandLastMessage = new byte[0]; } } else { DebugLog("X10 <", Utility.ByteArrayToString(message)); x10interface.WriteData(message); } } catch (Exception ex) { DebugLog("X10 !", ex.Message); DebugLog("X10 !", ex.StackTrace); gotReadWriteError = true; } Thread.Sleep(50); }
private void WriterThreadLoop() { while (true) { try { if (sendQueue.Count > 0) { if (communicationState != X10CommState.Ready) { Monitor.Enter(stateLock); //if (x10interface.GetType().Equals(typeof(CM15))) //{ // Monitor.Wait(stateLock, 3000); //} //else //{ Monitor.Wait(stateLock, 5000); //} communicationState = X10CommState.Ready; Monitor.Exit(stateLock); } byte[] msg = sendQueue.Dequeue(); SendMessage(msg); if (msg.Length > 1) { if (x10interface.GetType().Equals(typeof(CM11))) { expectedChecksum = (byte)((msg[0] + msg[1]) & 0xff); communicationState = X10CommState.WaitingChecksum; } else { communicationState = X10CommState.WaitingAck; } } } else { Thread.Sleep(100); } } catch (Exception ex) { DebugLog("X10 !", ex.Message); DebugLog("X10 !", ex.StackTrace); gotReadWriteError = true; Thread.Sleep(1000); } } }
private void SendMessage(byte[] message) { try { if (message.Length > 1 && IsConnected) { // Wait for message delivery acknowledge lock (waitAckMonitor) { // have a 500ms pause between each output message while ((DateTime.Now - lastReceivedTs).TotalMilliseconds < 500) { Thread.Sleep(1); } logger.Debug(BitConverter.ToString(message)); if (!x10interface.WriteData(message)) { logger.Warn("Interface I/O error"); } commandLastMessage = message; waitAckTimestamp = DateTime.Now; if (x10interface.GetType().Equals(typeof(CM11))) { expectedChecksum = (byte)((message[0] + message[1]) & 0xff); communicationState = X10CommState.WaitingChecksum; } else { communicationState = X10CommState.WaitingAck; } while (commandResendAttempts < commandResendMax && communicationState != X10CommState.Ready) { var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; while (elapsedFromWaitAck.TotalSeconds < commandTimeoutSeconds && communicationState != X10CommState.Ready) { Thread.Sleep(1); elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; } if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { // Resend last message commandResendAttempts++; logger.Warn("Previous command timed out, resending ({0})", commandResendAttempts); if (!x10interface.WriteData(commandLastMessage)) { logger.Warn("Interface I/O error"); } waitAckTimestamp = DateTime.Now; } } commandResendAttempts = 0; commandLastMessage = new byte[0]; } } else { logger.Debug(BitConverter.ToString(message)); if (!x10interface.WriteData(message)) { logger.Warn("Interface I/O error"); } } } catch (Exception ex) { logger.Error(ex); gotReadWriteError = true; } }
private void SetInterfaceReady() { Monitor.Enter(stateLock); communicationState = X10CommState.Ready; Monitor.Pulse(stateLock); Monitor.Exit(stateLock); }
private void ReaderThreadLoop() { while (true) { try { byte[] readData = x10interface.ReadData(); if (readData.Length > 0) { DebugLog("X10 >", Utility.ByteArrayToString(readData)); var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { DebugLog( "X10 >", "COMMAND TIMEOUT" ); communicationState = X10CommState.Ready; } // if (communicationState == X10CommState.WaitingAck && readData[0] == (int)X10CommandType.PLC_Ready && readData.Length <= 2) // ack received { DebugLog( "X10 >", "COMMAND SUCCESSFUL" ); communicationState = X10CommState.Ready; } else if ((readData.Length >= 13 || (readData.Length == 2 && readData[0] == 0xFF && readData[1] == 0x00)) && !isInterfaceReady) { UpdateInterfaceTime(false); isInterfaceReady = true; communicationState = X10CommState.Ready; } else if (readData.Length == 2 && communicationState == X10CommState.WaitingChecksum && readData[0] == expectedChecksum && readData[1] == 0x00) { // checksum is received only from CM11 DebugLog( "X10 >", "CKSUM: " + "Expected [" + Utility.ByteArrayToString(new byte[] { expectedChecksum }) + "] Checksum ==> " + Utility.ByteArrayToString(readData) ); //TODO: checksum verification not handled, we just reply 0x00 (OK) SendMessage(new byte[] { 0x00 }); communicationState = X10CommState.WaitingAck; } else if (readData[0] == (int)X10CommandType.Macro) { lastReceivedTs = DateTime.Now; DebugLog("X10 >", "MACRO: " + Utility.ByteArrayToString(readData)); } else if (readData[0] == (int)X10CommandType.RF) { lastReceivedTs = DateTime.Now; DebugLog("X10 >", "RFCOM: " + Utility.ByteArrayToString(readData)); if (RfDataReceived != null) { Thread signal = new Thread(() => { addressedModules.Clear(); RfDataReceived(new RfDataReceivedAction() { RawData = readData }); }); signal.Start(); } } else if ((readData[0] == (int)X10CommandType.PLC_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (byte)X10CommandType.PLC_ReplyToPoll }); // reply to poll } else if ((readData[0] == (int)X10CommandType.PLC_FilterFail_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (int)X10CommandType.PLC_FilterFail_Poll }); // reply to filter fail poll } else if ((readData[0] == (int)X10CommandType.PLC_Poll)) { lastReceivedTs = DateTime.Now; DebugLog("X10 >", "PLCRX: " + Utility.ByteArrayToString(readData)); // if (readData.Length > 3) { bool newAddressData = true; int messageLength = readData[1]; if (readData.Length > messageLength - 2) { char[] bitmapData = Convert.ToString(readData[2], 2).PadLeft(8, '0').ToCharArray(); byte[] functionBitmap = new byte[messageLength - 1]; for (int i = 0; i < functionBitmap.Length; i++) { functionBitmap[i] = byte.Parse(bitmapData[7 - i].ToString()); } byte[] messageData = new byte[messageLength - 1]; Array.Copy(readData, 3, messageData, 0, messageLength - 1); // CM15 Extended receive has got inverted data if (messageLength > 2 && x10interface.GetType().Equals(typeof(CM15))) { Array.Reverse(functionBitmap, 0, functionBitmap.Length); Array.Reverse(messageData, 0, messageData.Length); } DebugLog("X10 >", "FNMAP: " + Utility.ByteArrayToString(functionBitmap)); DebugLog("X10 >", " DATA: " + Utility.ByteArrayToString(messageData)); for (int b = 0; b < messageData.Length; b++) { // read current byte data (type: 0x00 address, 0x01 function) if (functionBitmap[b] == (byte)X10FunctionType.Address) // address { string housecode = ((X10HouseCodes)Convert.ToInt16( messageData[b].ToString("X2").Substring( 0, 1 ), 16 )).ToString(); string unitcode = ((X10UnitCodes)Convert.ToInt16( messageData[b].ToString("X2").Substring( 1, 1 ), 16 )).ToString(); if (unitcode.IndexOf("_") > 0) { unitcode = unitcode.Substring(unitcode.IndexOf("_") + 1); } // DebugLog("X10 >", " " + b + ") House code = " + housecode); DebugLog("X10 >", " " + b + ") Unit code = " + unitcode); // string currentUnitCode = housecode + unitcode; if (!moduleStatus.Keys.Contains(currentUnitCode)) { var module = new X10Module() { Code = currentUnitCode }; module.PropertyChanged += ModulePropertyChanged; moduleStatus.Add(currentUnitCode, module); } var mod = moduleStatus[currentUnitCode]; // //TODO: this needs more testing.... if (!addressedModules.ContainsKey(housecode)) { addressedModules.Add(housecode, new List <X10Module>()); } else if (newAddressData) { newAddressData = false; addressedModules[housecode].Clear(); } // if (!addressedModules[housecode].Contains(mod)) { addressedModules[housecode].Add(mod); } } else if (functionBitmap[b] == (byte)X10FunctionType.Function) // function { string currentCommand = ((X10Command)Convert.ToInt16( messageData[b].ToString("X2").Substring( 1, 1 ), 16 )).ToString().ToUpper(); string currentHouseCode = ((X10HouseCodes)Convert.ToInt16( messageData[b].ToString("X2").Substring( 0, 1 ), 16 )).ToString(); // DebugLog("X10 >", " " + b + ") House code = " + currentHouseCode); DebugLog("X10 >", " " + b + ") Command = " + currentCommand); // // //TODO: this needs more testing.... if (!addressedModules.ContainsKey(currentHouseCode)) { addressedModules.Add(currentHouseCode, new List <X10Module>()); } // switch (currentCommand) { case "ALL_UNITS_OFF": if (currentHouseCode != "") { AllUnitsOff(currentHouseCode); } break; case "ALL_LIGHTS_ON": if (currentHouseCode != "") { AllLightsOn(currentHouseCode); } break; case "ON": ModulesOn(currentHouseCode); break; case "OFF": ModulesOff(currentHouseCode); break; case "BRIGHT": ModulesBright(currentHouseCode, messageData[++b]); break; case "DIM": ModulesDim(currentHouseCode, messageData[++b]); break; } // newAddressData = true; } } } } } else if ((readData[0] == (int)X10CommandType.PLC_TimeRequest)) // IS THIS A TIME REQUEST? { UpdateInterfaceTime(false); } else { #region This is an hack for detecting disconnection status on Linux platforms if (readData[0] == 0x00) { zeroChecksumCount++; } else { zeroChecksumCount = 0; } // if (zeroChecksumCount > 10) { zeroChecksumCount = 0; gotReadWriteError = true; Close(); } #endregion } } } catch (Exception e) { if (!e.GetType().Equals(typeof(TimeoutException)) && !e.GetType().Equals(typeof(OverflowException))) { DebugLog("X10 !", e.Message); DebugLog("X10 !", e.StackTrace); gotReadWriteError = true; } } } }
private void SendMessage(byte[] message) { try { // Wait for message delivery acknowledge if (message.Length > 1 && IsConnected) { lock (waitAckMonitor) { while ((DateTime.Now - lastReceivedTs).TotalMilliseconds < 500) { Thread.Sleep(50); } DebugLog("X10 <", Utility.ByteArrayToString(message)); x10interface.WriteData(message); commandLastMessage = message; waitAckTimestamp = DateTime.Now; if (x10interface.GetType().Equals(typeof(CM11))) { expectedChecksum = (byte)((message[0] + message[1]) & 0xff); communicationState = X10CommState.WaitingChecksum; } else { communicationState = X10CommState.WaitingAck; } while (commandResendAttempts < commandResendMax && communicationState != X10CommState.Ready) { var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; while (elapsedFromWaitAck.TotalSeconds < commandTimeoutSeconds && communicationState != X10CommState.Ready) { Thread.Sleep(50); elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; } if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { // Resend last message commandResendAttempts++; DebugLog( "X10 >", "PREVIOUS COMMAND TIMED OUT, RESENDING(" + commandResendAttempts + ")" ); x10interface.WriteData(commandLastMessage); waitAckTimestamp = DateTime.Now; } } commandResendAttempts = 0; commandLastMessage = new byte[0]; } } else { DebugLog("X10 <", Utility.ByteArrayToString(message)); x10interface.WriteData(message); } } catch (Exception ex) { DebugLog("X10 !", ex.Message); DebugLog("X10 !", ex.StackTrace); gotReadWriteError = true; } Thread.Sleep(50); }
private void ReaderThreadLoop() { while (true) { try { byte[] readData = x10interface.ReadData(); if (readData.Length > 0) { Utility.DebugLog( "X10 >", Utility.ByteArrayToString(readData) ); // var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { Utility.DebugLog( "X10 >", "COMMAND TIMEOUT" ); communicationState = X10CommState.Ready; } // if (communicationState == X10CommState.WaitingAck && readData[0] == (int)X10CommandType.PLC_Ready && readData.Length <= 2) // ack received { Utility.DebugLog( "X10 >", "COMMAND SUCCESSFUL" ); communicationState = X10CommState.Ready; } else if ((readData.Length >= 13 || (readData.Length == 2 && readData[0] == 0xFF && readData[1] == 0x00)) && !isInterfaceReady) { UpdateInterfaceTime(false); isInterfaceReady = true; communicationState = X10CommState.Ready; } else if (readData.Length == 2 && communicationState == X10CommState.WaitingChecksum && readData[0] == expectedChecksum && readData[1] == 0x00) { // checksum is received only from CM11 Utility.DebugLog( "X10 >", "CKSUM: " + "Expected [" + Utility.ByteArrayToString(new byte[] { expectedChecksum }) + "] Checksum ==> " + Utility.ByteArrayToString(readData) ); //TODO: checksum verification not handled, we just reply 0x00 (OK) SendMessage(new byte[] { 0x00 }); communicationState = X10CommState.WaitingAck; } else if (readData[0] == (int)X10CommandType.Macro) { lastReceivedTs = DateTime.Now; Utility.DebugLog("X10 >", "MACRO: " + Utility.ByteArrayToString(readData)); } else if (readData[0] == (int)X10CommandType.RF) { lastReceivedTs = DateTime.Now; string message = Utility.ByteArrayToString(readData); Utility.DebugLog("X10 >", "RFCOM: " + message); // repeated messages check if (lastRfMessage == message && (lastReceivedTs - lastRfReceivedTs).TotalMilliseconds < 200) { Utility.DebugLog("X10 >", "RFCOM: ^^^^^^^^^^^^^^^^^ Ignoring repeated message within 200ms"); continue; } lastRfMessage = message; lastRfReceivedTs = lastReceivedTs; // if (RfDataReceived != null) { Thread signal = new Thread(() => { try { RfDataReceived(new RfDataReceivedAction() { RawData = readData }); } catch { // TODO: handle/report exception } }); signal.Start(); } // Decode X10 RF Module Command (eg. "5D 20 70 8F 48 B7") if (readData.Length == 6 && readData[1] == 0x20 && ((readData[3] & ~readData[2]) == readData[3] && (readData[5] & ~readData[4]) == readData[5])) { byte hu = readData[2]; // house code + 4th bit of unit code byte hf = readData[4]; // unit code (3 bits) + function code string houseCode = ((X10HouseCode)(Utility.ReverseByte((byte)(hu >> 4)) >> 4)).ToString(); switch (hf) { case 0x98: // DIM ONE STEP CommandEvent_Dim(0x0F); break; case 0x88: // BRIGHT ONE STEP CommandEvent_Bright(0x0F); break; case 0x90: // ALL LIGHTS ON if (houseCode != "") { CommandEvent_AllLightsOn(houseCode); } break; case 0x80: // ALL LIGHTS OFF if (houseCode != "") { CommandEvent_AllUnitsOff(houseCode); } break; default: string houseUnit = Convert.ToString(hu, 2).PadLeft(8, '0'); string unitFunction = Convert.ToString(hf, 2).PadLeft(8, '0'); string unitCode = (Convert.ToInt16(houseUnit.Substring(5, 1) + unitFunction.Substring(1, 1) + unitFunction.Substring(4, 1) + unitFunction.Substring(3, 1), 2) + 1).ToString(); // UnselectModules(); SelectModule(houseCode + unitCode); // if (unitFunction[2] == '1') // 1 = OFF, 0 = ON { CommandEvent_Off(); } else { CommandEvent_On(); } break; } } } else if ((readData[0] == (int)X10CommandType.PLC_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (byte)X10CommandType.PLC_ReplyToPoll }); // reply to poll } else if ((readData[0] == (int)X10CommandType.PLC_FilterFail_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (int)X10CommandType.PLC_FilterFail_Poll }); // reply to filter fail poll } else if ((readData[0] == (int)X10CommandType.PLC_Poll)) { lastReceivedTs = DateTime.Now; Utility.DebugLog("X10 >", "PLCRX: " + Utility.ByteArrayToString(readData)); // if (readData.Length > 3) { int messageLength = readData[1]; if (readData.Length > messageLength - 2) { char[] bitmapData = Convert.ToString(readData[2], 2).PadLeft(8, '0').ToCharArray(); byte[] functionBitmap = new byte[messageLength - 1]; for (int i = 0; i < functionBitmap.Length; i++) { functionBitmap[i] = byte.Parse(bitmapData[7 - i].ToString()); } byte[] messageData = new byte[messageLength - 1]; Array.Copy(readData, 3, messageData, 0, messageLength - 1); // // CM15 Extended receive has got inverted data if (messageLength > 2 && x10interface.GetType().Equals(typeof(CM15))) { Array.Reverse(functionBitmap, 0, functionBitmap.Length); Array.Reverse(messageData, 0, messageData.Length); } // Utility.DebugLog("X10 >", "FNMAP: " + Utility.ByteArrayToString(functionBitmap)); Utility.DebugLog("X10 >", " DATA: " + Utility.ByteArrayToString(messageData)); for (int b = 0; b < messageData.Length; b++) { // read current byte data (type: 0x00 address, 0x01 function) if (functionBitmap[b] == (byte)X10FunctionType.Address) // address { X10HouseCode houseCode = (X10HouseCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(0, 1), 16); X10UnitCode unitCode = (X10UnitCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(1, 1), 16); string address = Utility.HouseUnitCodeFromEnum(houseCode, unitCode); // Utility.DebugLog("X10 >", " " + b + ") Address = " + address); // if (newAddressData) { newAddressData = false; UnselectModules(); } SelectModule(address); } else if (functionBitmap[b] == (byte)X10FunctionType.Function) // function { string function = ((X10Command)Convert.ToInt16(messageData[b].ToString("X2").Substring(1, 1), 16)).ToString().ToUpper(); string houseCode = ((X10HouseCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(0, 1), 16)).ToString(); // Utility.DebugLog("X10 >", " " + b + ") House code = " + houseCode); Utility.DebugLog("X10 >", " " + b + ") Command = " + function); // switch (function) { case "ALL_UNITS_OFF": if (houseCode != "") { CommandEvent_AllUnitsOff(houseCode); } break; case "ALL_LIGHTS_ON": if (houseCode != "") { CommandEvent_AllLightsOn(houseCode); } break; case "ON": CommandEvent_On(); break; case "OFF": CommandEvent_Off(); break; case "BRIGHT": CommandEvent_Bright(messageData[++b]); break; case "DIM": CommandEvent_Dim(messageData[++b]); break; } // newAddressData = true; } } } } } else if ((readData[0] == (int)X10CommandType.PLC_TimeRequest)) // IS THIS A TIME REQUEST? { UpdateInterfaceTime(false); } else { #region This is an hack for detecting disconnection status on Linux platforms if (readData[0] == 0x00) { zeroChecksumCount++; } else { zeroChecksumCount = 0; } // if (zeroChecksumCount > 10) { zeroChecksumCount = 0; gotReadWriteError = true; Close(); } else { SendMessage(new byte[] { 0x00 }); } #endregion } } } catch (Exception e) { if (!e.GetType().Equals(typeof(TimeoutException)) && !e.GetType().Equals(typeof(OverflowException))) { gotReadWriteError = true; Utility.DebugLog("X10 !", e.Message); Utility.DebugLog("X10 !", e.StackTrace); } } } }
private void ReaderThreadLoop() { while (true) { try { byte[] readData = x10interface.ReadData(); if (readData.Length > 0) { Utility.DebugLog( "X10 >", Utility.ByteArrayToString(readData) ); // var elapsedFromWaitAck = DateTime.Now - waitAckTimestamp; if (elapsedFromWaitAck.TotalSeconds >= commandTimeoutSeconds && communicationState != X10CommState.Ready) { Utility.DebugLog( "X10 >", "COMMAND TIMEOUT" ); communicationState = X10CommState.Ready; } // if (communicationState == X10CommState.WaitingAck && readData[0] == (int)X10CommandType.PLC_Ready && readData.Length <= 2) // ack received { Utility.DebugLog( "X10 >", "COMMAND SUCCESSFUL" ); communicationState = X10CommState.Ready; } else if ((readData.Length >= 13 || (readData.Length == 2 && readData[0] == 0xFF && readData[1] == 0x00)) && !isInterfaceReady) { UpdateInterfaceTime(false); isInterfaceReady = true; communicationState = X10CommState.Ready; } else if (readData.Length == 2 && communicationState == X10CommState.WaitingChecksum && readData[0] == expectedChecksum && readData[1] == 0x00) { // checksum is received only from CM11 Utility.DebugLog( "X10 >", "CKSUM: " + "Expected [" + Utility.ByteArrayToString(new byte[] { expectedChecksum }) + "] Checksum ==> " + Utility.ByteArrayToString(readData) ); //TODO: checksum verification not handled, we just reply 0x00 (OK) SendMessage(new byte[] { 0x00 }); communicationState = X10CommState.WaitingAck; } else if (readData[0] == (int)X10CommandType.Macro) { lastReceivedTs = DateTime.Now; Utility.DebugLog("X10 >", "MACRO: " + Utility.ByteArrayToString(readData)); } else if (readData[0] == (int)X10CommandType.RF) { lastReceivedTs = DateTime.Now; string message = Utility.ByteArrayToString(readData); Utility.DebugLog("X10 >", "RFCOM: " + message); // repeated messages check if (lastRfMessage == message && (lastReceivedTs - lastRfReceivedTs).TotalMilliseconds < 200) { Utility.DebugLog("X10 >", "RFCOM: ^^^^^^^^^^^^^^^^^ Ignoring repeated message within 200ms"); continue; } lastRfMessage = message; lastRfReceivedTs = lastReceivedTs; // if (RfDataReceived != null) { Thread signal = new Thread(() => { try { RfDataReceived(new RfDataReceivedAction() { RawData = readData }); } catch { // TODO: handle/report exception } }); signal.Start(); } // Decode X10 RF Module Command (eg. "5D 20 70 8F 48 B7") if (readData.Length == 6 && readData[1] == 0x20 && ((readData[3] &~ readData[2]) == readData[3] && (readData[5] &~ readData[4]) == readData[5])) { byte hu = readData[2]; // house code + 4th bit of unit code byte hf = readData[4]; // unit code (3 bits) + function code string houseCode = ((X10HouseCode)(Utility.ReverseByte((byte)(hu >> 4)) >> 4)).ToString(); switch (hf) { case 0x98: // DIM ONE STEP CommandEvent_Dim(0x0F); break; case 0x88: // BRIGHT ONE STEP CommandEvent_Bright(0x0F); break; case 0x90: // ALL LIGHTS ON if (houseCode != "") CommandEvent_AllLightsOn(houseCode); break; case 0x80: // ALL LIGHTS OFF if (houseCode != "") CommandEvent_AllUnitsOff(houseCode); break; default: string houseUnit = Convert.ToString(hu, 2).PadLeft(8, '0'); string unitFunction = Convert.ToString(hf, 2).PadLeft(8, '0'); string unitCode = (Convert.ToInt16(houseUnit.Substring(5, 1) + unitFunction.Substring(1, 1) + unitFunction.Substring(4, 1) + unitFunction.Substring(3, 1), 2) + 1).ToString(); // UnselectModules(); SelectModule(houseCode + unitCode); // if (unitFunction[2] == '1') // 1 = OFF, 0 = ON { CommandEvent_Off(); } else { CommandEvent_On(); } break; } } } else if ((readData[0] == (int)X10CommandType.PLC_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (byte)X10CommandType.PLC_ReplyToPoll }); // reply to poll } else if ((readData[0] == (int)X10CommandType.PLC_FilterFail_Poll) && readData.Length <= 2) { isInterfaceReady = true; SendMessage(new byte[] { (int)X10CommandType.PLC_FilterFail_Poll }); // reply to filter fail poll } else if ((readData[0] == (int)X10CommandType.PLC_Poll)) { lastReceivedTs = DateTime.Now; Utility.DebugLog("X10 >", "PLCRX: " + Utility.ByteArrayToString(readData)); // if (readData.Length > 3) { int messageLength = readData[1]; if (readData.Length > messageLength - 2) { char[] bitmapData = Convert.ToString(readData[2], 2).PadLeft(8, '0').ToCharArray(); byte[] functionBitmap = new byte[messageLength - 1]; for (int i = 0; i < functionBitmap.Length; i++) { functionBitmap[i] = byte.Parse(bitmapData[7 - i].ToString()); } byte[] messageData = new byte[messageLength - 1]; Array.Copy(readData, 3, messageData, 0, messageLength - 1); // // CM15 Extended receive has got inverted data if (messageLength > 2 && x10interface.GetType().Equals(typeof(CM15))) { Array.Reverse(functionBitmap, 0, functionBitmap.Length); Array.Reverse(messageData, 0, messageData.Length); } // Utility.DebugLog("X10 >", "FNMAP: " + Utility.ByteArrayToString(functionBitmap)); Utility.DebugLog("X10 >", " DATA: " + Utility.ByteArrayToString(messageData)); for (int b = 0; b < messageData.Length; b++) { // read current byte data (type: 0x00 address, 0x01 function) if (functionBitmap[b] == (byte)X10FunctionType.Address) // address { X10HouseCode houseCode = (X10HouseCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(0, 1), 16); X10UnitCode unitCode = (X10UnitCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(1, 1), 16); string address = Utility.HouseUnitCodeFromEnum(houseCode, unitCode); // Utility.DebugLog("X10 >", " " + b + ") Address = " + address); // if (newAddressData) { newAddressData = false; UnselectModules(); } SelectModule(address); } else if (functionBitmap[b] == (byte)X10FunctionType.Function) // function { string function = ((X10Command)Convert.ToInt16(messageData[b].ToString("X2").Substring(1, 1), 16)).ToString().ToUpper(); string houseCode = ((X10HouseCode)Convert.ToInt16(messageData[b].ToString("X2").Substring(0, 1), 16)).ToString(); // Utility.DebugLog("X10 >", " " + b + ") House code = " + houseCode); Utility.DebugLog("X10 >", " " + b + ") Command = " + function); // switch (function) { case "ALL_UNITS_OFF": if (houseCode != "") CommandEvent_AllUnitsOff(houseCode); break; case "ALL_LIGHTS_ON": if (houseCode != "") CommandEvent_AllLightsOn(houseCode); break; case "ON": CommandEvent_On(); break; case "OFF": CommandEvent_Off(); break; case "BRIGHT": CommandEvent_Bright(messageData[++b]); break; case "DIM": CommandEvent_Dim(messageData[++b]); break; } // newAddressData = true; } } } } } else if ((readData[0] == (int)X10CommandType.PLC_TimeRequest)) // IS THIS A TIME REQUEST? { UpdateInterfaceTime(false); } else { #region This is an hack for detecting disconnection status on Linux platforms if (readData[0] == 0x00) { zeroChecksumCount++; } else { zeroChecksumCount = 0; } // if (zeroChecksumCount > 10) { zeroChecksumCount = 0; gotReadWriteError = true; Close(); } else { SendMessage(new byte[] { 0x00 }); } #endregion } } } catch (Exception e) { if (!e.GetType().Equals(typeof(TimeoutException)) && !e.GetType().Equals(typeof(OverflowException))) { gotReadWriteError = true; Utility.DebugLog("X10 !", e.Message); Utility.DebugLog("X10 !", e.StackTrace); } } } }