protected void WriteChannel(ModbusBaseClientStation self, IModbusMaster master, ModbusChannelImp ch)
        {
            try
            {
                ushort[] registers;
                byte[]   adr = new byte[4];
                byte[]   adr0;
                byte[]   adr1;
                bool     conv_ok = true;

                ushort startAddress = ch.ModbusDataAddress;
                ushort numInputs    = ch.DeviceDataLen;

                switch (ch.ModbusDataType)
                {
                case ModbusDataTypeEx.HoldingRegister:
                    switch (ch.DeviceDataType)
                    {
                    case ModbusDeviceDataType.Int:
                    case ModbusDeviceDataType.UInt:
                        if (ch.Type == typeof(int))             // ch.ModbusFs2InternalType == ModbusFs2InternalType.Int32)
                        {
                            int v = (int)ch.Value;
                            adr = BitConverter.GetBytes(v);
                        }
                        else if (ch.Type == typeof(uint))             // ch.ModbusFs2InternalType == ModbusFs2InternalType.Int32)
                        {
                            uint v = (uint)ch.Value;
                            adr = BitConverter.GetBytes(v);
                        }
                        else if (ch.Type == typeof(double))            // ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                        {
                            double d = (double)ch.Value;
                            d = (d - ch.D) / ch.K;
                            if (ch.DeviceDataType == ModbusDeviceDataType.Int)
                            {
                                short s = (short)d;
                                adr = BitConverter.GetBytes(s);
                            }
                            else
                            {
                                ushort s = (ushort)d;
                                adr = BitConverter.GetBytes(s);
                            }
                        }
                        else
                        {
                            if (self.LoggingLevel >= ModbusLog.logWarnings)
                            {
                                Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                            self.Name, ch.Name, ch.ModbusFs2InternalType.ToString(), ch.DeviceDataType.ToString()));
                            }
                            conv_ok = false;
                        }
                        if (conv_ok)
                        {
                            if (ch.ConversionType == ModbusConversionType.SwapBytes)
                            {
                                byte tmp = adr[0]; adr[0] = adr[1]; adr[1] = tmp;
                            }
                            master.WriteSingleRegister(ch.SlaveId, ch.ModbusDataAddress, BitConverter.ToUInt16(adr, 0));
                        }
                        break;

                    case ModbusDeviceDataType.DInt:
                    case ModbusDeviceDataType.DUInt:
                    case ModbusDeviceDataType.Float:
                        if (ch.Type == typeof(int))             // ch.ModbusFs2InternalType == ModbusFs2InternalType.Int32)
                        {
                            int v = (int)ch.Value;
                            adr = BitConverter.GetBytes(v);
                        }
                        else if (ch.Type == typeof(uint))             // ch.ModbusFs2InternalType == ModbusFs2InternalType.Int32)
                        {
                            uint v = (uint)ch.Value;
                            adr = BitConverter.GetBytes(v);
                        }
                        else if (ch.Type == typeof(double))            // ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                        {
                            double d = (double)ch.Value;
                            d = (d - ch.D) / ch.K;
                            if (ch.DeviceDataType == ModbusDeviceDataType.DInt)
                            {
                                int s = (int)d;
                                adr = BitConverter.GetBytes(s);
                            }
                            else if (ch.DeviceDataType == ModbusDeviceDataType.DUInt)
                            {
                                uint s = (uint)d;
                                adr = BitConverter.GetBytes(s);
                            }
                            else
                            {
                                //float
                                float s = (float)d;
                                adr = BitConverter.GetBytes(s);
                            }
                        }
                        else
                        {
                            if (self.LoggingLevel >= ModbusLog.logWarnings)
                            {
                                Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                            self.Name, ch.Name, ch.ModbusFs2InternalType.ToString(), ch.DeviceDataType.ToString()));
                            }
                            conv_ok = false;
                        }
                        if (conv_ok)
                        {
                            SwapBytesOut(adr, out adr0, out adr1, ch.ConversionType);
                            registers = new ushort[] { BitConverter.ToUInt16(adr0, 0), BitConverter.ToUInt16(adr1, 0) };
                            master.WriteMultipleRegisters(ch.SlaveId, ch.ModbusDataAddress, registers);
                        }
                        break;

                    case ModbusDeviceDataType.String:
                    case ModbusDeviceDataType.Bool:
                        if (self.LoggingLevel >= ModbusLog.logWarnings)
                        {
                            Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvertImpl,
                                                                        self.Name, ch.Name, ch.ModbusFs2InternalType.ToString(), ch.DeviceDataType.ToString()));
                        }
                        break;
                    }
                    break;

                case ModbusDataTypeEx.Coil:
                    if (ch.Type == typeof(bool))
                    {
                        master.WriteSingleCoil(ch.SlaveId, ch.ModbusDataAddress, (bool)ch.Value);
                    }
                    else
                    {
                        if (self.LoggingLevel >= ModbusLog.logWarnings)
                        {
                            Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                        self.Name, ch.Name, ch.ModbusFs2InternalType.ToString(), ch.DeviceDataType.ToString()));
                        }
                    }
                    break;
                }
            }
            catch (Modbus.SlaveException e)
            {
                if (self.LoggingLevel >= ModbusLog.logWarnings)
                {
                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrException,
                                                                self.Name, e.Message));
                }
            }
            catch (OverflowException e)
            {
                if (self.LoggingLevel >= ModbusLog.logWarnings)
                {
                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrException,
                                                                self.Name, e.Message));
                }
            }
            catch (TimeoutException e)
            {
                if (self.LoggingLevel >= ModbusLog.logWarnings)
                {
                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrException,
                                                                self.Name, e.Message));
                }
            }
            catch (InvalidCastException e)
            {
                if (self.LoggingLevel >= ModbusLog.logWarnings)
                {
                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrException,
                                                                self.Name, e.Message));
                }
            }
        }
        protected void ReadBuffer(ModbusBaseClientStation self, IModbusMaster master, ModbusBuffer buf)
        {
            try
            {
                ushort[] registers;
                byte[]   adr;
                bool[]   inputs;

                if (buf.pauseCounter == 0)
                {
                    ushort startAddress = buf.startAddress;
                    ushort numInputs    = buf.numInputs;
                    switch (buf.ModbusDataType)
                    {
                    case ModbusDataTypeEx.InputRegister:
                    case ModbusDataTypeEx.HoldingRegister:
                        if (buf.ModbusDataType == ModbusDataTypeEx.InputRegister)
                        {
                            registers = master.ReadInputRegisters(buf.slaveId, startAddress, numInputs);
                        }
                        else
                        {
                            registers = master.ReadHoldingRegisters(buf.slaveId, startAddress, numInputs);
                        }
                        DateTime dt      = DateTime.Now;
                        int      iresult = 0;
                        uint     uresult = 0;
                        double   fresult = 0.0;
                        foreach (ModbusChannelImp ch in buf.channels)
                        {
                            switch (ch.DeviceDataType)
                            {
                            case ModbusDeviceDataType.Int:
                                adr = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress]);
                                switch (ch.ConversionType)
                                {
                                case ModbusConversionType.SwapBytes:
                                    byte tmp = adr[0]; adr[0] = adr[1]; adr[1] = tmp;
                                    iresult = BitConverter.ToInt16(adr, 0);
                                    break;

                                default:
                                    iresult = BitConverter.ToInt16(adr, 0);
                                    break;
                                }
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Int32)
                                {
                                    ch.DoUpdate(iresult, dt, ChannelStatusFlags.Good);
                                }
                                else if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                                {
                                    ch.DoUpdate((double)(ch.K * iresult + ch.D), dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;

                            case ModbusDeviceDataType.UInt:
                                adr = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress]);
                                switch (ch.ConversionType)
                                {
                                case ModbusConversionType.SwapBytes:
                                    byte tmp = adr[0]; adr[0] = adr[1]; adr[1] = tmp;
                                    uresult = BitConverter.ToUInt16(adr, 0);
                                    break;

                                default:
                                    uresult = BitConverter.ToUInt16(adr, 0);
                                    break;
                                }
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.UInt32)
                                {
                                    ch.DoUpdate(uresult, dt, ChannelStatusFlags.Good);
                                }
                                else if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                                {
                                    ch.DoUpdate((double)(ch.K * uresult + ch.D), dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;

                            case ModbusDeviceDataType.DInt:
                                byte[] adr0 = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress]);
                                byte[] adr1 = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress + 1]);
                                byte[] res  = new byte[4];
                                res     = self.SwapBytesIn(adr0, adr1, ch.ConversionType);
                                iresult = BitConverter.ToInt32(res, 0);
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Int32)
                                {
                                    ch.DoUpdate(iresult, dt, ChannelStatusFlags.Good);
                                }
                                else if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                                {
                                    ch.DoUpdate((double)(ch.K * iresult + ch.D), dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;

                            case ModbusDeviceDataType.DUInt:

                                adr0    = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress]);
                                adr1    = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress + 1]);
                                res     = self.SwapBytesIn(adr0, adr1, ch.ConversionType);
                                uresult = BitConverter.ToUInt32(res, 0);
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.UInt32)
                                {
                                    ch.DoUpdate(uresult, dt, ChannelStatusFlags.Good);
                                }
                                else if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                                {
                                    ch.DoUpdate((double)(ch.K * uresult + ch.D), dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;

                            case ModbusDeviceDataType.Float:

                                adr0    = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress]);
                                adr1    = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress + 1]);
                                res     = self.SwapBytesIn(adr0, adr1, ch.ConversionType);
                                fresult = BitConverter.ToSingle(res, 0);
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Double)
                                {
                                    ch.DoUpdate((double)(ch.K * fresult + ch.D), dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;

                            case ModbusDeviceDataType.Bool:

                                bool bit = (registers[ch.ModbusDataAddress - buf.startAddress] & (0x01 << ch.BitIndex)) > 0;
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Boolean)
                                {
                                    ch.DoUpdate(bit, dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;

                            case ModbusDeviceDataType.String:

                                byte[]  str       = new byte[2 * ch.DeviceDataLen];
                                Decoder ascii     = (new ASCIIEncoding()).GetDecoder();
                                int     bytesUsed = 0;
                                int     charsUsed = 0;
                                bool    completed = false;
                                int     j         = 0;
                                // Conversion strategy: FIRST NONPRINTABLE CHARACTER (ORD < 32) BREAKS CONVERSION, string consists of printables converted before
                                for (int i = 0; i < ch.DeviceDataLen; i++)
                                {
                                    byte[] word = BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress + i]);
                                    if (ch.ConversionType == ModbusConversionType.SwapBytes)
                                    {
                                        if (word[1] < 32)
                                        {
                                            break;          // nonprintable character
                                        }
                                        str[j++] = word[1];
                                        if (word[0] < 32)
                                        {
                                            break;          // nonprintable character
                                        }
                                        str[j++] = word[0];
                                    }
                                    else
                                    {
                                        if (word[0] < 32)
                                        {
                                            break;          // nonprintable character
                                        }
                                        str[j++] = word[0];
                                        if (word[1] < 32)
                                        {
                                            break;          // nonprintable character
                                        }
                                        str[j++] = word[1];
                                        //Array.Copy(BitConverter.GetBytes(registers[ch.ModbusDataAddress - buf.startAddress + i]), 0, str, 2 * i, 2);
                                    }
                                }
                                string sresult;
                                if (j > 0)
                                {
                                    char[] chars = new char[j];
                                    ascii.Convert(str, 0, j, chars, 0, j, true, out bytesUsed, out charsUsed, out completed);
                                    sresult = new String(chars);
                                }
                                else
                                {
                                    sresult = "";
                                }
                                if (ch.ModbusFs2InternalType == ModbusFs2InternalType.String)
                                {
                                    ch.DoUpdate(sresult, dt, ChannelStatusFlags.Good);
                                }
                                else
                                if (self.LoggingLevel >= ModbusLog.logWarnings)
                                {
                                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                                self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                                }
                                break;
                            }
                        }
                        break;

                    case ModbusDataTypeEx.Coil:
                    case ModbusDataTypeEx.Input:
                        if (buf.ModbusDataType == ModbusDataTypeEx.Coil)
                        {
                            inputs = master.ReadCoils(buf.slaveId, startAddress, numInputs);
                        }
                        else
                        {
                            inputs = master.ReadInputs(buf.slaveId, startAddress, numInputs);
                        }
                        dt = DateTime.Now;
                        foreach (ModbusChannelImp ch in buf.channels)
                        {
                            if (ch.ModbusFs2InternalType == ModbusFs2InternalType.UInt32)
                            {
                                uint val = (uint)(inputs[ch.ModbusDataAddress - buf.startAddress] ? 1 : 0);
                                ch.DoUpdate(val, dt, ChannelStatusFlags.Good);
                            }
                            else if (ch.ModbusFs2InternalType == ModbusFs2InternalType.Boolean)
                            {
                                bool val = inputs[ch.ModbusDataAddress - buf.startAddress];
                                ch.DoUpdate(val, dt, ChannelStatusFlags.Good);
                            }
                            else
                            if (self.LoggingLevel >= ModbusLog.logWarnings)
                            {
                                Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrConvert,
                                                                            self.Name, ch.Name, ch.DeviceDataType.ToString(), ch.ModbusFs2InternalType.ToString()));
                            }
                        }
                        break;
                    }   // Case
                    if (self.failures.ContainsKey(buf.slaveId))
                    {
                        // failure signal defined
                        self.failures[buf.slaveId].Value = false;
                    }
                }   // If
                else
                {
                    buf.pauseCounter--;
                }
            }   // Try
            catch (Modbus.SlaveException e)
            {
                buf.pauseCounter = self.FailedCount;
                if (self.failures.ContainsKey(buf.slaveId))
                {
                    // failure signal defined
                    self.failures[buf.slaveId].Value = true;
                }
                foreach (ModbusChannelImp ch in buf.channels)
                {
                    ch.StatusFlags = ChannelStatusFlags.Bad;
                }
                if (self.LoggingLevel >= ModbusLog.logWarnings)
                {
                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrReceive,
                                                                self.Name, buf.slaveId, buf.ModbusDataType.ToString(), buf.startAddress, buf.numInputs, e.Message));
                }
            }
            catch (TimeoutException e)
            {
                buf.pauseCounter = self.FailedCount;
                if (self.failures.ContainsKey(buf.slaveId))
                {
                    // failure signal defined
                    self.failures[buf.slaveId].Value = true;
                }
                foreach (ModbusChannelImp ch in buf.channels)
                {
                    ch.StatusFlags = ChannelStatusFlags.Bad;
                }
                if (self.LoggingLevel >= ModbusLog.logWarnings)
                {
                    Env.Current.Logger.LogWarning(string.Format(StringConstants.ErrReceive,
                                                                self.Name, buf.slaveId, buf.ModbusDataType.ToString(), buf.startAddress, buf.numInputs, e.Message));
                }
            }
        }