/// <summary> /// Конструктор /// </summary> /// <param name="error">Код ошибки выполнения запроса</param> /// <param name="description">Описание ошибки</param> /// <param name="request">Запрос</param> /// <param name="answer">Ответное сообщение на запрос</param> public Result(Error error, String description, Message request, Message answer) { _Request = request; _ErrorCode = error; _Description = description; _Answer = answer; return; }
/// <summary> /// Конструктор /// </summary> /// <param name="message">Сообщение отправленное /// мастеру или принятое от него</param> public MessageEventArgs(Message.Message message) { Message = message; }
//--------------------------------------------------------------------------- Result INetworkFunctions.ReadHoldingRegisters(Message.Message request) { Message.Result result; Message.Message answer; Message.PDU pdu; String message; if (request.Address == 0) { // Ошибка. Данная команда не может быть широковещательная message = "Широковещательный запрос на выполнение функции 0x03 невозможен"; result = new Message.Result(Error.RequestError, message, request, null); } else { // Проверяем длину PDU (должна равнятся 5 байтам) if (request.PDUFrame.ToArray().Length != 5) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x3 равна {0} байт. Должна быть 5 байт", request.PDUFrame.ToArray().Length); result = new Message.Result(Error.DataFormatError, mes, request, null); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес первого регистра входа Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем количесво регистров для чтения Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 quantity = Modbus.Convert.ConvertToUInt16(array); if ((quantity == 0) || (quantity > 125)) { message = String.Format("Количество регистров для чтения в запросе {0}, а должно быть 1...125", quantity); pdu = new Message.PDU(0x83, new Byte[] { 0x03 }); answer = new Message.Message(_Address, pdu); // Отправляем сообщение SendResponse(answer); result = new Message.Result(Error.IllegalDataValue, message, request, answer); } else { // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов for (int i = 0; i < quantity; i++) { // Если регистр не существует в системе формируем ответ // c исключение if (HoldingRegisters.Contains(System.Convert.ToUInt16(address + i)) == false) { // Формируем ответ с исключение 2 pdu = new Message.PDU(0x83, new Byte[] { 0x02 }); answer = new Message.Message(_Address, pdu); message = String.Format("Регистр с адресом {0} не существует", (address + i)); result = new Message.Result(Error.IllegalDataAddress, message, request, answer); // Отправляем ответ мастеру SendResponse(answer); return result; } } // Все регистры найдены формируем ответное сообщение Byte[] temp; List<Byte> data = new List<byte>(); data.Add(System.Convert.ToByte((quantity * 2))); for (int i = 0; i < quantity; i++) { temp = Modbus.Convert.ConvertToBytes(HoldingRegisters[i].Value); data.AddRange(temp); } pdu = new Message.PDU(); pdu.Function = 0x3; pdu.AddDataBytesRange(data.ToArray()); answer = new Message.Message(_Address, pdu); result = new Message.Result(Error.NoError, String.Empty, request, answer); // Отправляем ответ мастеру SendResponse(answer); } } } return result; }
public void SendResponse(Message.Message answer) { // Останавливаем таймер таймаута _TimerTimeoutCurrentTransaction.Stop(); Byte[] array = answer.ToArray(); // Отсылаем ответ _SerialPort.Write(array, 0, array.Length); // Останавливаем транзакцию _СurrentTransaction.Stop(answer); // Формирует событие OnResponseWasSent(new MessageEventArgs(answer)); return; }
//--------------------------------------------------------------------------- #endregion // !!! Необоходимо сделать статическим методами, для того что бы уменьшить // размер устройтва в ОЗУ. #region INetworkFunctions Members //--------------------------------------------------------------------------- Result INetworkFunctions.ReadCoils(Modbus.OSIModel.Message.Message request) { Message.Result result; Message.Message answer; PDU pdu; String message; if (request.Address == 0) { // Ошибка. Данная команда не может быть широковещательная message = "Широковещательный запрос на выполнение функции 0x01 невозможен"; result = new Message.Result(Error.RequestError, message, request, null); } else { // Проверяем длину PDU (должна равнятся 5 байтам) if (request.PDUFrame.ToArray().Length != 5) { // Длина сообщения не верная message = String.Format( "Длина PDU-фрайма в запросе {0}. Должна быть 5 байт", request.PDUFrame.ToArray()); // Ответное сообщение не отсылается result = new Message.Result(Error.DataFormatError, message, request, null); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес первого реле Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем количесво реле для чтения Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 quantity = Modbus.Convert.ConvertToUInt16(array); if ((quantity == 0) || (quantity > 2000)) { message = String.Format( "Количество реле для чтения в запросе {0}, а должно быть 1...2000", quantity); pdu = new Message.PDU(0x81, new Byte[] { 0x03 }); answer = new Modbus.OSIModel.Message.Message(_Address, pdu); // Отправляем сообщение SendResponse(answer); result = new Message.Result(Error.IllegalDataValue, message, request, answer); } else { // Выполняем запрос. Проверяем доступность в // системе реле в указанном диапазоне адресов for (int i = 0; i < quantity; i++) { // Проверяем существует ли реле с указанным адресом. // Если реле не существует в системе формируем ответ // c исключение if (Coils.Contains(System.Convert.ToUInt16(address + i)) == false) { // Формируем ответ с исключение 2 answer = new Modbus.OSIModel.Message.Message(Address, new PDU(0x81, new Byte[] { 0x2 })); // Отправляем ответ мастеру SendResponse(answer); result = new Message.Result(Error.IllegalDataAddress, String.Format("Реле с адресом {0} не существует", System.Convert.ToUInt16(address + i)), request, answer); return result; } } // Все реле найдены формируем ответное сообщение int totalBytes = quantity % 8; if (totalBytes == 0) { totalBytes = quantity / 8; } else { totalBytes = 1 + (quantity / 8); } Byte[] data = new Byte[totalBytes]; int number = 0; int index = 0; for (int i = 0; i < quantity; i++) { data[index] = (Byte)(data[index] | (Byte)(Modbus.Convert.BooleanToBit( Coils[System.Convert.ToUInt16(address + i)].Value) << number)); if (++number > 7) { number = 0; ++index; } } pdu = new Message.PDU(); pdu.Function = 0x01; pdu.AddDataByte((byte)totalBytes); // Добавляем количество байт с состояниями реле pdu.AddDataBytesRange(data); // Добавляем байты с состояниями реле answer = new Modbus.OSIModel.Message.Message(Address, pdu); // Отправляем ответ мастеру SendResponse(answer); result = new Message.Result(Error.NoError, String.Empty, request, answer); } } } return result; }
//--------------------------------------------------------------------------- Result INetworkFunctions.ReadDiscreteInputs(Message.Message request) { Message.Result result; Message.Message answer; Message.PDU pdu; String message; if (request.Address == 0) { // Ошибка. Данная команда не может быть широковещательная message = "Широковещательный запрос на выполнение функции 0x02 невозможен"; result = new Message.Result(Error.RequestError, message, request, null); } else { // Проверяем длину PDU (должна равнятся 5 байтам) if (request.PDUFrame.ToArray().Length != 5) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x2 равна {0} байт. Должна быть 5 байт", request.PDUFrame.ToArray().Length); result = new Message.Result(Error.DataFormatError, mes, request, null); Debug.WriteLine(mes); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес первого дискретного входа Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем количесво дискретных входов для чтения Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 quantity = Modbus.Convert.ConvertToUInt16(array); if ((quantity == 0) || (quantity > 2000)) { message = String.Format("Количество дискретных входов для чтения в запросе {0}, а должно быть 1...2000", quantity); pdu = new Message.PDU(0x82, new Byte[] { 0x03 }); answer = new Modbus.OSIModel.Message.Message(_Address, pdu); // Отправляем сообщение SendResponse(answer); result = new Message.Result(Error.IllegalDataValue, message, request, answer); } else { // Выполняем запрос. Проверяем доступность в // системе дискретных входов в указанном диапазоне адресов //DiscreteInput[] inputs = new DiscreteInput[quantity]; for (int i = 0; i < quantity; i++) { //inputs[i] = _discretesInputs.Find((UInt16)(address + i)); if (DiscretesInputs.Contains(System.Convert.ToUInt16(address + i)) == false) { // Если дискретный вход не существует в системе формируем ответ // c исключением 2 pdu = new Message.PDU(0x82, new Byte[] { 0x02 }); answer = new Message.Message(_Address, pdu); message = String.Format("Дискретный вход с адресом {0} не существует", (address + i)); result = new Message.Result(Error.IllegalDataAddress, message, request, answer); // Отправляем ответ мастеру SendResponse(answer); return result; } } // Все дискретные входы найдены формируем ответное сообщение int totalBytes = quantity % 8; if (totalBytes == 0) { totalBytes = quantity / 8; if (totalBytes == 0) { totalBytes++; } } else { totalBytes = 1 + (quantity / 8); } Byte[] data = new Byte[totalBytes]; int number = 0; int index = 0; for (int i = 0; i < quantity; i++) { data[index] = (Byte)(data[index] | (Byte)(Modbus.Convert.BooleanToBit(DiscretesInputs[address + i].Value) << number)); if (++number > 7) { number = 0; ++index; } } pdu = new Message.PDU(); pdu.Function = request.PDUFrame.Function; pdu.AddDataByte((Byte)totalBytes); pdu.AddDataBytesRange(data); answer = new Message.Message(_Address, pdu); result = new Message.Result(Error.NoError, String.Empty, request, answer); // Отправляем ответ мастеру SendResponse(answer); } } } return result; }
/// <summary> /// Метод отправляет ответ slave-устройства мастеру сети на его запрос. /// </summary> /// <param name="answer">Ответное сообщение</param> private void SendResponse(Message.Message answer) { if (NetworkController != null) { NetworkController.GetOutcommingMessage(answer); } else { throw new NullReferenceException( "Невозможно отправить ответ мастеру на запрос. Недоступен контроллер сети"); } return; }
/// <summary> /// Забирает запрос и вызывает необходимые /// для его выполнения функции /// </summary> /// <param name="request">Запрос от мастера</param> protected virtual void RequestParse(Message.Message request) { Message.Result result; // Получаем код команды и анализируем сообщение switch (request.PDUFrame.Function) { case 0x01: // Функция 0x1. Чтение реле (не может быть широковещательной) { result = ((INetworkFunctions)this).ReadCoils(request); break; } case 0x02: // Функция 0x2. Читает дискретные входы (не может быть широковещательной) { result = ((INetworkFunctions)this).ReadDiscreteInputs(request); break; } case 0x03: // Функция 0х3. Читает holding-регистры (не может быть широковещательной) { result = ((INetworkFunctions)this).ReadHoldingRegisters(request); break; } case 0x04: // Функция 0х4. Читает входные регистры (не может быть широковещательной) { result = ((INetworkFunctions)this).ReadInputRegisters(request); break; } case 0x05: // Функция 0х5. Устанавливает реле в состояние вкл./выкл. { result = ((INetworkFunctions)this).WriteSingleCoil(request); break; } case 0x06: // Функция 0x6. Записывает значение в одиночный регистр { result = ((INetworkFunctions)this).WriteSingleRegister(request); break; } case 0x0F: { result = ((INetworkFunctions)this).WriteMultipleCoils(request); break; } case 0x10: { result = ((INetworkFunctions)this).WriteMultipleRegisters(request); break; } case 0x14: { result = ((INetworkFunctions)this).ReadFileRecord(request); break; } default: { result = ((INetworkFunctions)this).FunctionNotSupported(request); break; } } }
//--------------------------------------------------------------------------- Result INetworkFunctions.FunctionNotSupported(Message.Message request) { Message.Message answer; Message.PDU pdu = new Message.PDU(); pdu.Function = (Byte)(request.PDUFrame.Function | 0x80); pdu.AddDataByte(0x01); //Error.IllegalFunction answer = new Modbus.OSIModel.Message.Message(Address, pdu); // Отправляем ответ SendResponse(answer); Message.Result result = new Message.Result(Error.IllegalFunction, "Функция не поддерживается данным устройством", request, answer); return result; }
/// <summary> /// Метод получает входящие из сети сообщение (от мастера) обрабатываем его /// и формирует ответ, если это необходимо (при адресованном запросе) /// </summary> /// <param name="message">Входящие сообщение</param> /// <returns>Исходящие ответное сообщение</returns> internal void GetIncommingMessage(Message.Message message) { // Проверяем статус устройства, если устройство пассивно // не обрабатываем запрос и не отвечаем. if (Status == Status.Running) { // Проверяем. Это сообщение адресовано данному устройству или нет if ((message.Address == Address) || (message.Address == 0)) { // Сообщение предназначено для данного устройства // !!! данную функцию выполнить в отдельном потоке RequestParse(message); } else { // Нет, это сообщение не предназначено для данного устройства } } return; }
//--------------------------------------------------------------------------- Result INetworkFunctions.ReadFileRecord(Message.Message request) { Message.Result result; Message.Message answer; //Message.PDU pdu; String message; UInt16 var; Code0x14SubRequest[] subRequestList; const int ByteCountIndex = 0; // Индекс параметра Byte Count в массиве данных запроса if (request.Address == 0) { // Ошибка. Данная команда не может быть широковещательная message = "Широковещательный запрос на выполнение функции 0x14 невозможен"; result = new Message.Result(Error.RequestError, message, request, null); return result; } else { if (request.PDUFrame.Length < 7) { // Ошибка. Минимальная длина pdu запроса 0х14 это 7 байт message = "Ошибка. Длина pdu запроса 0x14 менее 7 байт"; answer = new Message.Message(_Address, new PDU((Byte)(0x14 | 0x80), new byte[] { 0x03 })); result = new Message.Result(Error.RequestError, message, request, answer); // Отправляем ответ SendResponse(answer); return result; } else { if ((request.PDUFrame.Data[ByteCountIndex] < 0x07) || (request.PDUFrame.Data[ByteCountIndex] > 0xF5)) { // Ошибка. Указанная длина Byte Count вне допустимого диапазона значений message = String.Format( "Ошибка. Код функции 0x14. Параметр Byte Count = {0} вне допустимого диапазона 0x07...0xF5", request.PDUFrame.Data[ByteCountIndex], (request.PDUFrame.Length - 2)); answer = new Message.Message(_Address, new PDU((Byte)(0x14 | 0x80), new byte[] { 0x03 })); result = new Message.Result(Error.RequestError, message, request, answer); // Отправляем ответ SendResponse(answer); return result; } else { // Проверяем фактическую длину запроса и величину переданную // в запросе "Byte Count" if ((Int32)request.PDUFrame.Data[ByteCountIndex] != (request.PDUFrame.Length - 2)) { // Ошибка. Указанная длина не совпадает с фактической message = String.Format( "Ошибка. Параметр Byte Count = {0} не совпадает с фактической длиной {1}", request.PDUFrame.Data[ByteCountIndex], (request.PDUFrame.Length - 2)); answer = new Message.Message(_Address, new PDU((Byte)(0x14 | 0x80), new byte[] { 0x03 })); result = new Message.Result(Error.RequestError, message, request, answer); // Отправляем ответ SendResponse(answer); return result; } else { // Определяем количество групп (подзапросов) в данном запросе if ((request.PDUFrame.Data.Length - 1) % 7 != 0) { // Длина подзапроса всегда равна 7, поэтому должно быть // кратно. Если при делении на 7, остаток не равен 0, то // данные не корректны // Ошибка. Указанная длина не совпадает с фактической message = String.Format("Ошибка. Неверная длина данных в запросе"); answer = new Message.Message(_Address, new PDU((Byte)(0x14 | 0x80), new byte[] { 0x03 })); result = new Message.Result(Error.RequestError, message, request, answer); // Отправляем ответ SendResponse(answer); return result; } else { // Подзапросы имеют корректный формат. Получаем их // Получаем количество подзапросов subRequestList = new Code0x14SubRequest[((request.PDUFrame.Data.Length - 1) / 7)]; // Получаем сами подзапросы for (int i = 0; i < subRequestList.Length; i++) { subRequestList[i].ReferenceType = request.PDUFrame.Data[(7 * i + 1)]; var = (UInt16)(((UInt16)request.PDUFrame.Data[(7 * i + 2)]) << 8); // Hi var = (UInt16)(var | request.PDUFrame.Data[(7 * i + 3)]); // Lo subRequestList[i].FileNumber = var; var = (UInt16)(((UInt16)request.PDUFrame.Data[(7 * i + 4)]) << 8); // Hi var = (UInt16)(var | request.PDUFrame.Data[(7 * i + 5)]); // Lo subRequestList[i].RecordNumber = var; var = (UInt16)(((UInt16)request.PDUFrame.Data[(7 * i + 6)]) << 8); // Hi var = (UInt16)(var | request.PDUFrame.Data[(7 * i + 7)]); // Lo subRequestList[i].RecordLength = var; } // Проверяем корректность данных в подзапросах for (int i = 0; i < subRequestList.Length; i++) { // Проверяем значение параметра "The reference type". Оно всегда // должно равняться 0x6 if (subRequestList[i].ReferenceType != 0x6) { // Ошибка answer = new Message.Message(_Address, new PDU((Byte)(0x80 | 0x14), new byte[] { 0x2 })); message = "Ошибка: Подзапрос имеет недопустимое значение праметра The reference type"; result = new Result(Error.RequestError, message, request, answer); SendResponse(answer); return result; } else { // Проверяем адрес старовтовой записы. Должен быть // не более 10000 (0x270F) if (subRequestList[i].RecordNumber > 0x270F) { // Ошибка answer = new Message.Message(_Address, new PDU((Byte)(0x80 | 0x14), new byte[] { 0x2 })); message = "Ошибка: Подзапрос имеет недопустимое значение номера записи файла"; result = new Result(Error.RequestError, message, request, answer); SendResponse(answer); return result; } else { // Проверяем конечный адрес (Начальный адрес + длина блока) if (subRequestList[i].RecordNumber + subRequestList[i].RecordLength > 0x270F) { // Ошибка answer = new Message.Message(_Address, new PDU((Byte)(0x80 | 0x14), new byte[] { 0x2 })); message = "Ошибка: Подзапрос имеет недопустимое значение длины блока читаемых записей из файла"; result = new Result(Error.RequestError, message, request, answer); SendResponse(answer); return result; } else { // Все проверки пройдены. Теперь можно получать конечные // значения } } } } // Определяем общую длину ответа и проверяем превышает ли она допустимый размер var = 2; // Function code + Resp. Data length for (int i = 0; i < subRequestList.Length; i++) { var = System.Convert.ToUInt16(var + 2); // File resp. length + Ref. Type var = System.Convert.ToUInt16(var + (subRequestList[i].RecordLength * 2)); } if (var > 253) { // Ошибка. Длина запрощенных данных превышает максимальную // длину PDU ответного пакета answer = new Message.Message(_Address, new PDU((Byte)(0x80 | 0x14), new byte[] { 0x2 })); message = "Ошибка: Длина запрошенных данных превышает максимальную длину PDU"; result = new Result(Error.RequestError, message, request, answer); SendResponse(answer); return result; } else { // Если оказались в этой точке, значит запрос корректен. Получаем значения Code0x14SubRequest subRequest; List<byte> list = new List<byte>(var); list.Add(System.Convert.ToByte(var - 2)); // Поле Resp. data length for (int i = 0; i < subRequestList.Length; i++) { subRequest = subRequestList[i]; if (_Files.Contains(subRequest.FileNumber) == true) { // Файл найден. Получаем его записи list.Add(System.Convert.ToByte(subRequest.RecordLength * 2 + 1)); // File resp. length list.Add(0x06); // Ref. type File file = _Files[subRequest.FileNumber]; for (int y = 0; y < subRequest.RecordLength; y++) { // Проверяем существует ли запись с данным номером var = System.Convert.ToUInt16( subRequest.RecordNumber + System.Convert.ToUInt16(y)); if (file.Records.Contains(var) == true) { // Указанная запись существует. Получаем её значение // Сохраняем в ответе в виде последоватлеьности байт list.AddRange(Modbus.Convert.ConvertToBytes( file.Records[var].Value)); } else { // Указанной записи не существует в данном файле // Ошибка. Длина запрощенных данных превышает максимальную // длину PDU ответного пакета answer = new Message.Message(_Address, new PDU((Byte)(0x80 | 0x14), new byte[] { 0x4 })); message = String.Format( "Ошибка: Не найдена запрашиваемая запись с номером {0} в запрашиваемом файле c номером {0}", var, file.Number); result = new Result(Error.RequestError, message, request, answer); SendResponse(answer); return result; } } } else { // Файла с таким номером не существует. Возвращает исключение answer = new Message.Message(_Address, new PDU((Byte)(0x80 | 0x14), new byte[] { 0x4 })); message = String.Format( "Ошибка: Не найден запрашиваемый файл c номером {0}", subRequest.FileNumber); result = new Result(Error.RequestError, message, request, answer); SendResponse(answer); return result; } } // Если оказались в данной точке, значит предыдущий цикл выполнился устпешно // Можно отправлять данные answer = new Message.Message(_Address, new PDU(0x14, list.ToArray())); message = String.Empty; result = new Result(Error.NoError, message, request, answer); SendResponse(answer); return result; } } } } } } //return result; }
//--------------------------------------------------------------------------- Result INetworkFunctions.WriteMultipleRegisters(Message.Message request) { Message.Result result; Message.Message answer; Message.PDU pdu; String message; // Проверяем длину PDU (должна быть не менее 8 байтам) if (request.PDUFrame.ToArray().Length < 8) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x10 равна {0} байт. Должна быть не менее 8 байт", request.PDUFrame.ToArray().Length); result = new Message.Result(Error.DataFormatError, mes, request, null); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес первого регистра Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем количесво регистров для записи Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 quantity = Modbus.Convert.ConvertToUInt16(array); // Получаем количество байт в пасылке Byte count = request.PDUFrame.Data[4]; if ((quantity == 0) || (quantity > 123) || ((quantity * 2) != count)) { message = String.Format("Количество регистров для записи в запросе равно {0}, а должно быть 1...123", quantity); pdu = new Message.PDU((Byte)(0x10 | 0x80), new Byte[] { 0x03 }); answer = new Modbus.OSIModel.Message.Message(_Address, pdu); // Отправляем сообщение SendResponse(answer); result = new Message.Result(Error.IllegalDataValue, message, request, answer); } else { // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов for (int i = 0; i < quantity; i++) { // Если регистр не существует в системе формируем ответ // c исключение if (HoldingRegisters.Contains(System.Convert.ToUInt16(address + i)) == false) { // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x10 | 0x80), new Byte[] { 0x02 }); answer = new Message.Message(_Address, pdu); message = String.Format("Регистр с адресом {0} не существует", (address + i)); result = new Message.Result(Error.IllegalDataAddress, message, request, answer); // Отправляем ответ мастеру SendResponse(answer); return result; } } // Все регистры найдены, устанавливаем новые значения и // формируем ответное сообщение Byte[] temp = new Byte[2]; List<Byte> data = new List<byte>(); // Устанавливаем новые значения в регистры for (int i = 0; i < quantity; i++) { Array.Copy(request.PDUFrame.Data, (5 + (i * 2)), temp, 0, 2); _HoldingRegisters[System.Convert.ToUInt16(address + i)].Value = Modbus.Convert.ConvertToUInt16(temp); } // Формируем ответ. temp = Modbus.Convert.ConvertToBytes(address); data.AddRange(temp); temp = Modbus.Convert.ConvertToBytes(quantity); data.AddRange(temp); pdu = new Message.PDU(); pdu.Function = 0x10; pdu.AddDataBytesRange(data.ToArray()); answer = new Message.Message(_Address, pdu); result = new Message.Result(Error.NoError, String.Empty, request, answer); // Отправляем ответ мастеру SendResponse(answer); // Формируем событие OnMasterChangedHoldingRegisters(); } } return result; }
//--------------------------------------------------------------------------- Result INetworkFunctions.WriteMultipleCoils(Message.Message request) { Message.Result result; Message.Message answer; Message.PDU pdu; String message; // Проверяем длину PDU (должна быть не менее 7 байтам) if (request.PDUFrame.ToArray().Length < 7) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x0F равна {0} байт. Должна быть не менее 7 байт", request.PDUFrame.ToArray().Length); result = new Message.Result(Error.DataFormatError, mes, request, null); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес первого реле Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем количесво реле для записи Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 quantity = Modbus.Convert.ConvertToUInt16(array); // Получаем количество байт в пасылке Byte count = request.PDUFrame.Data[4]; int totalBytes = quantity % 8; if (totalBytes == 0) { totalBytes = quantity / 8; } else { totalBytes = 1 + (quantity / 8); } if ((quantity == 0) || (quantity > 0x7B0) || (totalBytes != count)) { message = String.Format("Количество реле для записи в запросе равно {0}, а должно быть 1...0x7B0", quantity); pdu = new Message.PDU((Byte)(0xF | 0x80), new Byte[] { 0x03 }); answer = new Message.Message(_Address, pdu); // Отправляем сообщение SendResponse(answer); result = new Message.Result(Error.IllegalDataValue, message, request, answer); } else { // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов for (int i = 0; i < quantity; i++) { // Если регистр не существует в системе формируем ответ // c исключение if (Coils.Contains(System.Convert.ToUInt16(address + i)) == false) { // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x80 | 0x0F), new Byte[] { 0x02 }); answer = new Message.Message(Address, pdu); message = String.Format("Реле с адресом {0} не существует", (address + i)); result = new Message.Result(Error.IllegalDataAddress, message, request, answer); // Отправляем ответ мастеру SendResponse(answer); return result; } } // Все реле найдены, устанавливаем новые значения и // формируем ответное сообщение Byte status; // Устанавливаем новые значения в реле for (int i = 0; i < count; i++) { status = request.PDUFrame.Data[5 + i]; for (int y = 0; y < 8; y++) { totalBytes = i * 8 + y; if (totalBytes < quantity) { if (((status >> y) & 0x01) == 0) { Coils[System.Convert.ToUInt16(address + totalBytes)].Value = Modbus.Convert.ToBoolean(State.Off); //rows[totalBytes]["Value"] = Modbus.Convert.ToBoolean(State.Off); } else { Coils[System.Convert.ToUInt16(address + totalBytes)].Value = Modbus.Convert.ToBoolean(State.On); //rows[totalBytes]["Value"] = Modbus.Convert.ToBoolean(State.On); } } } } // Формируем ответ. List<Byte> data_ = new List<byte>(); data_.AddRange(Modbus.Convert.ConvertToBytes(address)); data_.AddRange(Modbus.Convert.ConvertToBytes(quantity)); pdu = new Message.PDU(); pdu.Function = 0x0F; pdu.AddDataBytesRange(data_.ToArray()); answer = new Message.Message(_Address, pdu); result = new Message.Result(Error.NoError, String.Empty, request, answer); // Отправляем ответ мастеру SendResponse(answer); // Формируем событие OnMasterChangedCoils(); } } return result; }
//--------------------------------------------------------------------------- Result INetworkFunctions.WriteSingleRegister(Message.Message request) { Message.Result result; Message.Message answer; Message.PDU pdu; String message; // Проверяем длину PDU (должна быть 5 байт) if (request.PDUFrame.ToArray().Length != 5) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x06 равна {0} байт. Должна быть 5 байт", request.PDUFrame.ToArray().Length); result = new Message.Result(Error.DataFormatError, mes, request, null); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес регистра Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем значение регистра Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 value = Modbus.Convert.ConvertToUInt16(array); // Выполняем запрос. Проверяем доступность в // регистра if (_HoldingRegisters.Contains(address) == false) { // Если регистр не существует в системе формируем ответ // c исключение // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x06 | 0x80), new Byte[] { 0x02 }); answer = new Message.Message(_Address, pdu); message = String.Format("Регистр с адресом {0} не существует", address); result = new Message.Result(Error.IllegalDataAddress, message, request, answer); // Отправляем ответ мастеру SendResponse(answer); return result; } else { // Все регистры найдены, устанавливаем новые значения и // формируем ответное сообщение _HoldingRegisters[address].Value = value; // Формируем ответ. pdu = new Message.PDU(); pdu.Function = 0x06; pdu.AddDataBytesRange(Modbus.Convert.ConvertToBytes(address)); pdu.AddDataBytesRange(Modbus.Convert.ConvertToBytes(value)); answer = new Modbus.OSIModel.Message.Message(_Address, pdu); result = new Message.Result(Error.NoError, String.Empty, request, answer); // Отправляем ответ мастеру SendResponse(answer); // Формируем событие OnMasterChangedHoldingRegisters(); } } return result; }
//--------------------------------------------------------------------------- Result INetworkFunctions.WriteSingleCoil(Message.Message request) { Message.Result result; Message.Message answer; Message.PDU pdu; String message; // Проверяем длину PDU (должна быть 5 байт) if (request.PDUFrame.ToArray().Length != 5) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x05 равна {0} байт. Должна быть 5 байт", request.PDUFrame.ToArray().Length); result = new Message.Result(Error.DataFormatError, mes, request, null); } else { // Разбираем сообщение Byte[] array = new Byte[2]; // Получаем адрес реле Array.Copy(request.PDUFrame.Data, 0, array, 0, 2); UInt16 address = Modbus.Convert.ConvertToUInt16(array); // Получаем значение реле для записи Array.Copy(request.PDUFrame.Data, 2, array, 0, 2); UInt16 status = Modbus.Convert.ConvertToUInt16(array); State coilValue; // Получаем количество байт в пасылке switch (status) { case 0x0000: { coilValue = State.Off; break; } case 0xFF00: { coilValue = State.On; break; } default: { // Ошибка message = String.Format( "Неверный формат данных для кодировки состояния реле {0}, а должно быть 0x0000 или 0xFF00", status.ToString("X4")); pdu = new Message.PDU((Byte)(0x05 | 0x80), new Byte[] { 0x03 }); answer = new Modbus.OSIModel.Message.Message(_Address, pdu); // Отправляем сообщение SendResponse(answer); result = new Message.Result(Error.IllegalDataValue, message, request, answer); return result; } } // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов // Если регистр не существует в системе формируем ответ // c исключение if (Coils.Contains(address) == false) { // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x80 | 0x05), new Byte[] { 0x02 }); answer = new Modbus.OSIModel.Message.Message(_Address, pdu); message = String.Format("Реле с адресом {0} не существует", address); result = new Message.Result(Error.IllegalDataAddress, message, request, answer); // Отправляем ответ мастеру SendResponse(answer); return result; } else { // Реле найдено, устанавливаем новое значение и // формируем ответное сообщение Coils[address].Value = Modbus.Convert.ToBoolean(coilValue); // Формируем ответ. List<Byte> data_ = new List<byte>(); data_.AddRange(Modbus.Convert.ConvertToBytes(address)); data_.AddRange(Modbus.Convert.StateToArray(coilValue)); pdu = new Message.PDU(); pdu.Function = 0x05; pdu.AddDataBytesRange(data_.ToArray()); answer = new Message.Message(_Address, pdu); result = new Message.Result(Error.NoError, String.Empty, request, answer); // Отправляем ответ мастеру SendResponse(answer); // Формируем событие OnMasterChangedCoils(); } } return result; }