/// <summary> /// Should be called every frame. /// Continiues execution, depending on what the previous message returned /// </summary> public void Update() { if (!Running) { return; } if (!messages.ContainsKey(CurrentState)) { DeveloppmentTools.Log(string.Format("State {0} not an option", CurrentState)); return; } if (messages[CurrentState].Length <= messgecycle) { DeveloppmentTools.Log(string.Format("MessageID {0} not an option", messgecycle)); return; } Message curr_message = messages[CurrentState][messgecycle]; Message.Result res = curr_message.Execute(console.HasInput ? console.ReadLine() : null); switch (res) { default: case Message.Result.error: case Message.Result.finished: if (++messgecycle >= messages [CurrentState].Length) { if (nextstate == ushort.MaxValue) { LogError("State not set"); } CurrentState = nextstate; messgecycle = 0; // To be recognized as the error-source nextstate = ushort.MaxValue; } break; case Message.Result.running: break; case Message.Result.break_: CurrentState = nextstate; break; case Message.Result.notimplemented: LogError("Function not implemented yet"); goto case Message.Result.error; case Message.Result.exit: Exit(); break; } return; }
//--------------------------------------------------------------------------- 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; }
//--------------------------------------------------------------------------- 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; }
//--------------------------------------------------------------------------- #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.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; }
//--------------------------------------------------------------------------- Message.Result IModbusFunctions.WriteSingleRegister(Message.Message request) { Message.Result result; 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); 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 value = Modbus.Convert.ConvertToUInt16(array); // Выполняем запрос. Проверяем доступность в // регистра //HoldingRegister register; Message.PDU pdu; //register = _holdingRegisters.Find(address); DataRow row = _dataMap.Tables["HoldingRegisters"].Rows.Find(address); // Если регистр не существует в системе формируем ответ // c исключение if (row == null) { // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x06 | 0x80), new Byte[] { 0x02 }); message = String.Format("Регистр с адресом {0} не существует", address); result = new Message.Result(Error.IllegalDataAddress, message, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); Debug.WriteLine(message); return result; } else { // Все регистры найдены, устанавливаем новые значения и // формируем ответное сообщение row["Value"] = value; // Формируем ответ. pdu = new Message.PDU(); pdu.Function = 0x06; pdu.AddDataBytesRange(Modbus.Convert.ConvertToBytes(address)); pdu.AddDataBytesRange(Modbus.Convert.ConvertToBytes(value)); result = new Message.Result(Error.NoError, String.Empty, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); // Формируем событие this.OnWriteHoldingRegisters(); } } return result; }
//--------------------------------------------------------------------------- /// <summary> /// Функция 0x6. Записывает значение в одиночный регистр /// хранения в удалённом устройстве /// </summary> /// <param name="request">Запрос на выполнение операции</param> /// <param name="value">Записанное в устройство значение регистра</param> /// <returns>Результат выполнения запроса</returns> public Modbus.OSIModel.Message.Result WriteSingleRegister(Message.Message request, ref UInt16 value) { Modbus.OSIModel.Message.Message answer; Modbus.OSIModel.Message.Result result; // Проверяем состояние сервера if (_flgBusy == true) { value = 0; return result = new Message.Result(Error.IllegalReguest, String.Format("Попытка выполнить запрос (код функции 0x6) к серверу {0}, во время выполнения предыдущего", Name), null, null); //throw new Exception(String.Format( // "Попытка выполнить запрос (код функции 0x6) к серверу {0}, во время выполнения предыдущего", // Name)); } else { // Устанавливаем начало транзакции StartTransaction(); // Отправляем запрос Modbus.OSIModel.DataLinkLayer.RequestError error = _dataLinkObject.SendMessage(request, out answer); switch (error) { case Modbus.OSIModel.DataLinkLayer.RequestError.NoError: { // Разбираем сообщение на предмет ошибок if (IsError(request, answer, out result)) { // Ошибка была value = 0; break; } else { // Ошибки нет возвращаем результат выполнения запроса // Проверяем длину сообщения if (answer.PDUFrame.Data.Length != 4) { value = 0; result = new Message.Result(Error.DataFormatError, String.Format( "Длина ответного сообщения {0} не соответствет ожидаемой 4", answer.PDUFrame.Data.Length), request, answer); } else { // Проверяем адрес регистра в ответном сообщениии Byte[] arr = new Byte[2]; Array.Copy(answer.PDUFrame.Data, 0, arr, 0, 2); value = Modbus.Convert.ConvertToUInt16(arr); Array.Copy(request.PDUFrame.Data, 0, arr, 0, 2); UInt16 var = Modbus.Convert.ConvertToUInt16(arr); if (value != var) { result = new Message.Result(Error.DataFormatError, String.Format( "Адрес регистра {0} в запросе не совпадает с аресом в ответном сообщении {1}", var, value), request, answer); } else { Array.Copy(answer.PDUFrame.Data, 2, arr, 0, 2); value = Modbus.Convert.ConvertToUInt16(arr); result = new Modbus.OSIModel.Message.Result(Error.NoError, String.Empty, request, answer); } } break; } } case Modbus.OSIModel.DataLinkLayer.RequestError.TimeOut: { // Таймаут ответа value = 0; result = new Modbus.OSIModel.Message.Result(Error.TimeOut, "Ответ не был получен в заданное время", request, null); break; } default: { // Ошибка уровня Datalink layer. value = 0; result = new Modbus.OSIModel.Message.Result(Error.ReciveMessageError, error.ToString(), request, null); break; } } StopTransaction(); return result; } }
/// <summary> /// Функция 0x2. Читает дискретные входы /// в удалённом устройстве /// </summary> /// <param name="AddressSlave">адрес удалённого slave-устройства, /// из которого нужно получить данные</param> /// <param name="StartingAddress">Адрес начального дискретного входа</param> /// <param name="Quantity">Количество дискретных входов для чтения 1...2000</param> /// <param name="inputs">Состояния прочитанных дискретных входов</param> /// <returns>Результат выполения операции</returns> public Message.Result ReadDiscreteInputs(byte AddressSlave, UInt16 StartingAddress, UInt16 Quantity, out State[] inputs) { Modbus.OSIModel.Message.Message request, answer; Modbus.OSIModel.Message.Result result; inputs = null; String message; // Данный запрос не может быть широковещательным if (AddressSlave == 0) { message = "Функция с кодом 0х2, не может быть широковещательным запросом"; throw new ArgumentException(message, "addressSlave"); } // Подготавливаем данные List<byte> array = new List<byte>(); array.AddRange(Modbus.Convert.ConvertToBytes(StartingAddress)); array.AddRange(Modbus.Convert.ConvertToBytes(Quantity)); request = new Modbus.OSIModel.Message.Message(AddressSlave, 0x2, array.ToArray()); // Выполняем запрос while (_flgBusy) { // Если идёт транзакция ждём окончания } // Устанавливаем начало транзакции StartTransaction(); // Отправляем запрос Modbus.OSIModel.DataLinkLayer.RequestError error = _dataLinkObject.SendMessage(request, out answer); switch (error) { case Modbus.OSIModel.DataLinkLayer.RequestError.NoError: { // Разбираем сообщение на предмет ошибок if (IsError(request, answer, out result)) { // Ошибка была break; } else { // Ошибки нет возвращаем результат выполнения запроса // Проверяем содержимое посылки // Рассчитываем необходимое количество байт для передачи состояний дискретных входов int length = Quantity / 8; if (Quantity % 8 != 0) { ++length; } if ((int)answer.PDUFrame.Data[0] != length) { result = new Message.Result( Error.DataFormatError, String.Format("Количество байт {0} в сообщении не равно требуемому {1} для передачи состояний реле в количестве {2}", answer.PDUFrame.Data[0], length, Quantity), request, answer); } else { Byte data, temp; inputs = new State[Quantity]; for (int i = 0; i < answer.PDUFrame.Data[0]; i++) { data = answer.PDUFrame.Data[i + 1]; for (int y = 0; y < 8; y++) { if (8 * i + y < Quantity) { temp = (Byte)(data >> y); if ((temp & 0x01) == 0x01) { inputs[(8 * i + y)] = State.On; } else { inputs[(8 * i + y)] = State.Off; } } } } } result = new Message.Result(Error.NoError, String.Empty, request, answer); break; } } case Modbus.OSIModel.DataLinkLayer.RequestError.TimeOut: { // Таймаут ответа result = new Modbus.OSIModel.Message.Result(Error.TimeOut, "Ответ не был получен в заданное время", request, null); break; } default: { // Ошибка уровня Datalink layer. result = new Modbus.OSIModel.Message.Result(Error.ReciveMessageError, error.ToString(), request, null); break; } } StopTransaction(); 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; }
//--------------------------------------------------------------------------- Message.Result IModbusFunctions.ReadInputRegisters(Message.Message request) { Message.Result result; String message; if (request.Address == 0) { // Ошибка. Данная команда не может быть широковещательная message = "Широковещательный запрос на выполнение функции 0x04 невозможен"; Debug.WriteLine(message); result = new Message.Result(Error.RequestError, message, request, null); } else { // Проверяем длину PDU (должна равнятся байтам) if (request.PDUFrame.ToArray().Length != 5) { // Длина сообщения не верная String mes = String.Format( "Длина PDU-фрайма равна в запросе 0x4 равна {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 > 125)) { message = String.Format("Количество регистров для чтения в запросе {0}, а должно быть 1...125", quantity); Message.PDU pdu = new Message.PDU(0x84, new Byte[] { 0x03 }); // Отправляем сообщение _connection.SendReply(pdu); result = new Message.Result(Error.IllegalDataValue, message, request, pdu); Debug.WriteLine(message); } else { // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов //InputRegister[] registers = new InputRegister[quantity]; DataRow[] rows = new DataRow[quantity]; Message.PDU pdu; for (int i = 0; i < quantity; i++) { //registers[i] = _inputRegisters.Find((UInt16)(address + i)); rows[i] = _dataMap.Tables["InputRegisters"].Rows.Find(address + i); // Если регистр не существует в системе формируем ответ // c исключение if (rows[i] == null) { // Формируем ответ с исключение 2 pdu = new Message.PDU(0x84, new Byte[] { 0x02 }); message = String.Format("Регистр с адресом {0} не существует", (address + i)); result = new Message.Result(Error.IllegalDataAddress, message, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); Debug.WriteLine(message); return result; } } // Все регистры найдены формируем ответное сообщение Byte[] temp; List<Byte> data = new List<byte>(); for (int i = 0; i < quantity; i++) { temp = Modbus.Convert.ConvertToBytes((UInt16)rows[i]["Value"]); data.AddRange(temp); } pdu = new Message.PDU(); pdu.Function = 0x04; pdu.AddDataByte((Byte)(data.Count)); pdu.AddDataBytesRange(data.ToArray()); result = new Message.Result(Error.NoError, String.Empty, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); } } } return result; }
//--------------------------------------------------------------------------- #endregion //--------------------------------------------------------------------------- #region Члены IApi //--------------------------------------------------------------------------- Message.Result IModbusFunctions.ReadCoils(Message.Message request) { Message.Result result; String message; if (request.Address == 0) { // Ошибка. Данная команда не может быть широковещательная message = "Широковещательный запрос на выполнение функции 0x01 невозможен"; result = new Message.Result(Error.RequestError, message, request, null); Debug.WriteLine(message); } 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); Message.PDU pdu = new Message.PDU(0x81, new Byte[] { 0x03 }); // Отправляем сообщение _connection.SendReply(pdu); Debug.WriteLine(message); result = new Message.Result(Error.IllegalDataValue, message, request, pdu); } else { // Выполняем запрос. Проверяем доступность в // системе реле в указанном диапазоне адресов DataRow[] row = new DataRow[quantity]; Message.PDU pdu; for (int i = 0; i < quantity; i++) { row[i] = _dataMap.Tables["Coils"].Rows.Find(i + address); // Если реле не существует в системе формируем ответ // c исключение if (row[i] == null) { // Формируем ответ с исключение 2 pdu = new Message.PDU(); pdu.Function = 0x01; pdu.SetErrorBit(); pdu.AddDataByte(0x02); result = new Message.Result(Error.IllegalDataAddress, String.Format("Реле с адресом {0} не существует", (address + i)), request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); 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((Boolean)row[i]["Value"]) << number)); if (++number > 7) { number = 0; ++index; } } pdu = new Message.PDU(); pdu.Function = 0x01; pdu.AddDataByte((byte)totalBytes); // Добавляем количество байт с состояниями реле pdu.AddDataBytesRange(data); // Добавляем байты с состояниями реле result = new Message.Result(Error.NoError, String.Empty, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); } } } return result; }
//--------------------------------------------------------------------------- Message.Result IModbusFunctions.FunctionNotSupported(Message.Message request) { Message.PDU pdu = new Message.PDU(); pdu.Function = (Byte)(request.PDUFrame.Function | 0x80); pdu.AddDataByte(0x01); //Error.IllegalFunction Message.Result result = new Message.Result(Error.IllegalFunction, "Функция не поддерживается данным устройством", request, pdu); // Отправляем ответ this._connection.SendReply(pdu); return result; }
//--------------------------------------------------------------------------- Message.Result IModbusFunctions.WriteMultipleRegisters(Message.Message request) { Message.Result result; 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); 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); // Получаем количество байт в пасылке Byte count = request.PDUFrame.Data[4]; if ((quantity == 0) || (quantity > 123) || ((quantity * 2) != count)) { message = String.Format("Количество регистров для записи в запросе равно {0}, а должно быть 1...123", quantity); Message.PDU pdu = new Message.PDU((Byte)(0x10 | 0x80), new Byte[] { 0x03 }); // Отправляем сообщение _connection.SendReply(pdu); result = new Message.Result(Error.IllegalDataValue, message, request, pdu); Debug.WriteLine(message); } else { // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов //HoldingRegister[] registers = new HoldingRegister[quantity]; DataRow[] rows = new DataRow[quantity]; Message.PDU pdu; for (int i = 0; i < quantity; i++) { //registers[i] = _holdingRegisters.Find((UInt16)(address + i)); rows[i] = _dataMap.Tables["HoldingRegisters"].Rows.Find((UInt16)(address + i)); // Если регистр не существует в системе формируем ответ // c исключение if (rows[i] == null) { // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x10 | 0x80), new Byte[] { 0x02 }); message = String.Format("Регистр с адресом {0} не существует", (address + i)); result = new Message.Result(Error.IllegalDataAddress, message, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); Debug.WriteLine(message); 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); rows[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()); result = new Message.Result(Error.NoError, String.Empty, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); // Формируем событие this.OnWriteHoldingRegisters(); } } return result; }
//--------------------------------------------------------------------------- /// <summary> /// Функция 16 (0х10). Записывает значения массива регистров /// </summary> /// <param name="AddressSlave">Адрес удалённого slave-устройства, /// в котором необходимо установить значения последовательности /// регистров</param> /// <param name="StartingAddress">Адрес первого регистра</param> /// <param name="value">Количество, значения регистров 1...123</param> /// <returns>Результат выполнения операции</returns> public Modbus.OSIModel.Message.Result WriteMultipleRegisters(byte AddressSlave, UInt16 StartingAddress, UInt16[] value) { String message; Message.Message request; Modbus.OSIModel.Message.Message answer; Modbus.OSIModel.Message.Result result; if ((value.Length == 0) && (value.Length > 124)) { message = String.Format( "Длина блока регистров {0} выходит за пределы диапазона 1...124", value.Length); throw new ArgumentOutOfRangeException("Quantity", message); } // Проверяем выход блока данных за граници диапазона допустимых адресов if ((StartingAddress + value.Length) > 0xFFFF) { message = String.Format( "Длина блока регистров {0} больше допустимой величины 0xFFFF", (StartingAddress + value.Length)); throw new ArgumentException(message, "Quantity"); } // Формируем запрос List<Byte> data = new List<byte>(); data.AddRange(Convert.ConvertToBytes(StartingAddress)); // Адрес первого регистра в блоке data.AddRange(Convert.ConvertToBytes((UInt16)value.Length)); // Длина блока регистров data.Add((Byte)(value.Length * 2)); // Количество байт // Значения регистров for (int i = 0; i < value.Length; i++) { data.AddRange(Convert.ConvertToBytes(value[i])); } Message.PDU pdu = new Message.PDU(0x10, data.ToArray()); request = new Message.Message(AddressSlave, pdu); // Проверяем состояние сервера if (_flgBusy == true) { return result = new Message.Result(Error.IllegalReguest, String.Format("Попытка выполнить запрос (код функции 0x10) к серверу {0}, во время выполнения предыдущего", Name), null, null); } else { // Устанавливаем начало транзакции StartTransaction(); // Отправляем запрос Modbus.OSIModel.DataLinkLayer.RequestError error = _dataLinkObject.SendMessage(request, out answer); switch (error) { case Modbus.OSIModel.DataLinkLayer.RequestError.NoError: { // Разбираем сообщение на предмет ошибок if (IsError(request, answer, out result)) { // Ошибка была break; } else { // Ошибки нет возвращаем результат выполнения запроса // Проверяем длину сообщения if (answer.PDUFrame.Data.Length != 4) { result = new Message.Result(Error.DataFormatError, String.Format( "Длина ответного сообщения {0} не соответствет ожидаемой 4", answer.PDUFrame.Data.Length), request, answer); } else { // Проверяем адрес начального регистра в ответном сообщениии Byte[] arr = new Byte[2]; Array.Copy(answer.PDUFrame.Data, 0, arr, 0, 2); UInt16 var = Modbus.Convert.ConvertToUInt16(arr); if (var != StartingAddress) { result = new Message.Result(Error.DataFormatError, String.Format( "Адрес начального регистра {0} в запросе не совпадает с адресом в ответном сообщении {1}", StartingAddress, var), request, answer); } else { // Проверяем длину блока регистров Array.Copy(answer.PDUFrame.Data, 2, arr, 0, 2); var = Modbus.Convert.ConvertToUInt16(arr); if (var != value.Length) { result = new Message.Result(Error.DataFormatError, String.Format( "Длина блока регистров {0} в запросе не совпадает с длиной блока в ответном сообщении {1}", value.Length, var), request, answer); } else { // Ответ корректен result = new Modbus.OSIModel.Message.Result(Error.NoError, String.Empty, request, answer); } } } break; } } case Modbus.OSIModel.DataLinkLayer.RequestError.TimeOut: { // Таймаут ответа result = new Modbus.OSIModel.Message.Result(Error.TimeOut, "Ответ не был получен в заданное время", request, null); break; } default: { // Ошибка уровня Datalink layer. result = new Modbus.OSIModel.Message.Result(Error.ReciveMessageError, error.ToString(), request, null); break; } } StopTransaction(); return result; } }
//--------------------------------------------------------------------------- /// <summary> /// Функция 0х1. Чтение реле /// </summary> /// <param name="addressSlave">адрес удалённого slave-устройства, /// из которого нужно получить данные</param> /// <param name="startingAddress">Адрес начального реле</param> /// <param name="quantity">Количество реле для чтения 1...2000</param> /// <param name="answer">Ответное сообщение</param> /// <param name="coils">Прочитанные состояния реле</param> /// <returns>Результат выполнения операции</returns> public Modbus.OSIModel.Message.Result ReadCoils( Modbus.OSIModel.Message.Message request, out State[] coils) { Modbus.OSIModel.Message.Message answer; Modbus.OSIModel.Message.Result result; Byte[] arrTemp = new Byte[2]; Array.Copy(request.PDUFrame.Data, 2, arrTemp, 0, 2); UInt16 quantity = Modbus.Convert.ConvertToUInt16(arrTemp); coils = new State[quantity]; while (_flgBusy) { // Если идёт транзакция ждём окончания } // Устанавливаем начало транзакции StartTransaction(); // Отправляем запрос Modbus.OSIModel.DataLinkLayer.RequestError error = _dataLinkObject.SendMessage(request, out answer); switch (error) { case Modbus.OSIModel.DataLinkLayer.RequestError.NoError: { // Разбираем сообщение на предмет ошибок if (IsError(request, answer, out result)) { // Ошибка была break; } else { // Ошибки нет возвращаем результат выполнения запроса // Проверяем содержимое посылки // Рассчитываем необходимое количество байт для передачи состояний реле int length = quantity / 8; if (quantity % 8 != 0) { ++length; } if ((int)answer.PDUFrame.Data[0] != length) { result = new Message.Result(Error.DataFormatError, String.Format( "Количество байт {0} в сообщении не равно требуемому {1} для передачи состояний реле в количестве {2}", answer.PDUFrame.Data[0], length, quantity), request, answer); } else { Byte data, temp; for (int i = 0; i < answer.PDUFrame.Data[0]; i++) { data = answer.PDUFrame.Data[i + 1]; for (int y = 0; y < 8; y++) { if (8 * i + y < quantity) { temp = (Byte)(data >> y); if ((temp & 0x01) == 0x01) { coils[(8 * i + y)] = State.On; } else { coils[(8 * i + y)] = State.Off; } } } } } result = new Message.Result(Error.NoError, String.Empty, request, answer); break; } } case Modbus.OSIModel.DataLinkLayer.RequestError.TimeOut: { // Таймаут ответа result = new Modbus.OSIModel.Message.Result(Error.TimeOut, "Ответ не был получен в заданное время", request, null); break; } default: { // Ошибка уровня Datalink layer. result = new Modbus.OSIModel.Message.Result(Error.ReciveMessageError, error.ToString(), request, null); break; } } StopTransaction(); 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; }
/// <summary> /// Функция 0х5. Устанавливает реле в состояние вкл./выкл. /// в удалённом устройстве /// </summary> /// <param name="addressSlave">адрес удалённого slave-устройства, /// в котором нужно установить состояние реле</param> /// <param name="addressCoil">Адрес целевого реле</param> /// <param name="state">Состояние реле: 0х00FF-ON, 0x0000-OFF</param> public Message.Result WriteSingleCoil(byte addressSlave, UInt16 addressCoil, ref State state) { string message; Modbus.OSIModel.Message.Message request, answer; Modbus.OSIModel.Message.Result result; // Проверяем состояние сервера if (_flgBusy == true) { return result = new Message.Result(Error.IllegalReguest, String.Format("Попытка выполнить запрос (код функции 0x5) к серверу {0}, во время выполнения предыдущего", Name), null, null); //throw new Exception(String.Format( // "Попытка выполнить запрос (код функции 0x5) к серверу {0}, во время выполнения предыдущего", // Name)); } else { // Данный запрос не может быть широковещательным if (addressSlave == 0) { message = "Функция с кодом 0х5, не может быть отправлена как широковещательный запрос"; throw new Exception(message); } else { // Устанавливаем начало транзакции StartTransaction(); // Подготавливаем данные List<byte> array = new List<byte>(); array.AddRange(Modbus.Convert.ConvertToBytes(addressCoil)); //Byte[] state; //if (ValueCoil == Modbus.Device.CoilState.ON) //{ // state = new byte[2] { 0xFF, 0x00 }; // array.AddRange(state); //} //else //{ // state = new byte[2] { 0x00, 0x00 }; // array.AddRange(state); //} array.AddRange(Modbus.Convert.StateToArray(state)); // Отправляем запрос request = new Modbus.OSIModel.Message.Message(addressSlave, 0x5, array.ToArray()); // Отправляем запрос Modbus.OSIModel.DataLinkLayer.RequestError error = _dataLinkObject.SendMessage(request, out answer); switch (error) { case Modbus.OSIModel.DataLinkLayer.RequestError.NoError: { // Разбираем сообщение на предмет ошибок if (IsError(request, answer, out result)) { // Ошибка была break; } else { // Ошибки нет возвращаем результат выполнения запроса // Проверяем длину данных if (answer.PDUFrame.Data.Length == 4) { // Длина корректана проверяем // адрес дискретного входа List<Byte> arr = new List<byte>(); arr.Add(answer.PDUFrame.Data[0]); arr.Add(answer.PDUFrame.Data[1]); UInt16 value = Convert.ConvertToUInt16(arr.ToArray()); if (value == addressCoil) { // Адрес верный // получаем новое состояние реле arr.Clear(); arr.Add(answer.PDUFrame.Data[2]); arr.Add(answer.PDUFrame.Data[3]); state = Convert.ValueToState(arr.ToArray()); result = new Modbus.OSIModel.Message.Result(Error.NoError, String.Empty, request, answer); } else { // Адрес дискретного входа отличается от адреса в запросе result = new Modbus.OSIModel.Message.Result(Error.DataFormatError, String.Format( "Адрес реле в запросе {0} не соответствует адерсу реле в ответе {1}", addressCoil, value), request, answer); } } else { // Некорректная длина данных в ответе result = new Modbus.OSIModel.Message.Result(Error.DataFormatError, String.Format( "Неверная длина данных {0} в ответе, а ожидается 4", answer.PDUFrame.Data.Length), request, answer); } break; } } case Modbus.OSIModel.DataLinkLayer.RequestError.TimeOut: { // Таймаут ответа result = new Modbus.OSIModel.Message.Result(Error.TimeOut, "Ответ не был получен в заданное время", request, null); break; } default: { // Ошибка уровня Datalink layer. result = new Modbus.OSIModel.Message.Result(Error.ReciveMessageError, error.ToString(), request, null); break; } } StopTransaction(); 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.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; }
//--------------------------------------------------------------------------- Message.Result IModbusFunctions.WriteSingleCoil(Message.Message request) { Message.Result result; 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); Debug.WriteLine(mes); } else { Message.PDU pdu; // Разбираем сообщение 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 statusOutput; // Получаем количество байт в пасылке switch (status) { case 0x0000: { statusOutput = State.Off; break; } case 0xFF00: { statusOutput = State.On; break; } default: { // Ошибка message = String.Format( "Неверный формат данных для кодировки состояния реле {0}, а должно быть 0x0000 или 0xFF00", status.ToString("X4")); pdu = new Message.PDU((Byte)(0x05 | 0x80), new Byte[] { 0x03 }); // Отправляем сообщение _connection.SendReply(pdu); result = new Message.Result(Error.IllegalDataValue, message, request, pdu); Debug.WriteLine(message); return result; } } // Выполняем запрос. Проверяем доступность в // системе регистров в указанном диапазоне адресов //Coil coil = _coils.Find((UInt16)(address)); DataRow row = _dataMap.Tables["Coils"].Rows.Find((UInt16)(address)); // Если регистр не существует в системе формируем ответ // c исключение if (row == null) { // Формируем ответ с исключение 2 pdu = new Message.PDU((Byte)(0x80 | 0x05), new Byte[] { 0x02 }); message = String.Format("Реле с адресом {0} не существует", address); result = new Message.Result(Error.IllegalDataAddress, message, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); Debug.WriteLine(message); return result; } else { // Реле найдено, устанавливаем новое значение и // формируем ответное сообщение row["Value"] = Modbus.Convert.ToBoolean(statusOutput); // Формируем ответ. List<Byte> data_ = new List<byte>(); data_.AddRange(Modbus.Convert.ConvertToBytes(address)); data_.AddRange(Modbus.Convert.StateToArray(statusOutput)); pdu = new Message.PDU(); pdu.Function = 0x05; pdu.AddDataBytesRange(data_.ToArray()); result = new Message.Result(Error.NoError, String.Empty, request, pdu); // Отправляем ответ мастеру this._connection.SendReply(pdu); // Формируем событие this.OnWriteCoils(); } } return result; }