public int Start()
        {
            //MessageBox.Show("Starting Modbus station " + name, "Info");
            buffers.Clear();

            if (channels.Count > 0)
            {
                //// Data Space analyse
                channels.Sort(channels[0].Compare);

                foreach (ModbusChannelImp ch in channels)
                {
                    bool found = false;
                    foreach (ModbusBuffer buf in buffers)
                    {
                        if (ch.ModbusDataType == buf.ModbusDataType)
                        {
                            int mult;
                            if (ch.ModbusDataType == ModbusDataType.Input || ch.ModbusDataType == ModbusDataType.Coil)
                            {
                                mult = 8;
                            }
                            else
                            {
                                mult = 1;
                            }
                            if (((ch.ModbusDataAddress - buf.lastAddress) < 4 * mult) && (buf.numInputs < 110 * mult))   // Optimization - "holes" in address space less than 4 bytes
                            // will be included in one read until max frame 2*110+4 is reached
                            {
                                buf.lastAddress = ch.ModbusDataAddress;
                                buf.numInputs   = (ushort)(buf.lastAddress - buf.startAddress + 1);
                                buf.channels.Add(ch);
                                found = true;
                            }
                        }
                    }
                    if (!found)
                    {
                        // Set up a new buffer
                        ModbusBuffer buf = new ModbusBuffer();
                        buf.numInputs      = 1;
                        buf.startAddress   = buf.lastAddress = ch.ModbusDataAddress;
                        buf.ModbusDataType = ch.ModbusDataType;
                        buf.channels.Add(ch);
                        buffers.Add(buf);
                    }
                }
                //// Run Thread
                channelUpdaterThread = new Thread(new ParameterizedThreadStart(ChannelUpdaterThreadProc));
                channelUpdaterThread.Start(this);
                return(0);
            }
            return(1);
        }
        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));
                }
            }
        }
        public int Start()
        {
            //MessageBox.Show("Starting Modbus station " + name, "Info");
            if (!StationActive)
            {
                return(1);
            }

            buffers.Clear();
            failures.Clear();
            sendQueue.Clear();

            if (channels.Count > 0)
            {
                //// Data Space analyse
                channels.Sort(channels[0].Compare);

                foreach (ModbusChannelImp ch in channels)
                {
                    if (ch.ModbusReadWrite != ModbusReadWrite.WriteOnly && ch.ModbusDataType != ModbusDataTypeEx.DeviceFailureInfo)
                    {
                        bool found = false;
                        foreach (ModbusBuffer buf in buffers)
                        {
                            if (ch.SlaveId == buf.slaveId && ch.ModbusDataType == buf.ModbusDataType)
                            {
                                int mult;
                                if (ch.ModbusDataType == ModbusDataTypeEx.Input || ch.ModbusDataType == ModbusDataTypeEx.Coil)
                                {
                                    mult = 16;
                                }
                                else
                                {
                                    mult = 1;
                                }
                                if (((ch.ModbusDataAddress - buf.lastAddress) < 4 * mult) && ((buf.lastAddress - buf.startAddress + ch.DeviceDataLen) < 120 * mult))
                                // Optimization - "holes" in address space less than 4 words
                                // will be included in one read until max frame 2*120 bytes is reached
                                {
                                    buf.lastAddress = (ushort)((ch.ModbusDataAddress + ch.DeviceDataLen - 1) > buf.lastAddress ? ch.ModbusDataAddress + ch.DeviceDataLen - 1 : buf.lastAddress);
                                    buf.numInputs   = (ushort)((buf.lastAddress - buf.startAddress + ch.DeviceDataLen) > buf.numInputs ? buf.lastAddress - buf.startAddress + ch.DeviceDataLen : buf.numInputs);
                                    buf.channels.Add(ch); ch.StatusFlags = ChannelStatusFlags.Bad;
                                    found = true;
                                }
                            }
                        }
                        if (!found)
                        {
                            // Set up a new buffer
                            ModbusBuffer buf = new ModbusBuffer();
                            buf.slaveId        = ch.SlaveId;
                            buf.numInputs      = ch.DeviceDataLen;
                            buf.startAddress   = ch.ModbusDataAddress;
                            buf.lastAddress    = (ushort)(ch.ModbusDataAddress + ch.DeviceDataLen - 1);
                            buf.ModbusDataType = ch.ModbusDataType;
                            buf.channels.Add(ch); ch.StatusFlags = ChannelStatusFlags.Bad;
                            buf.pauseCounter = 0;
                            buffers.Add(buf);
                        }
                    }
                    if (ch.ModbusDataType == ModbusDataTypeEx.DeviceFailureInfo)
                    {
                        if (failures.ContainsKey(ch.SlaveId))
                        {
                            // failure signal already defined, parameterization error
                            if (LoggingLevel >= ModbusLog.logErrors)
                            {
                                Env.Current.Logger.LogError(string.Format(StringConstants.ErrFailureTwice, this.Name, failures[ch.SlaveId].Name, ch.Name));
                            }
                        }
                        else
                        {
                            failures.Add(ch.SlaveId, ch);
                            ch.Value = true;
                        }
                    }
                }
                return(0);
            }
            return(1);
        }