/// <summary> /// Разбирает ответное сообщение /// </summary> /// <param name="message">Ответное сообщение</param> /// <returns>Структура данных ответа</returns> internal static IncomingMessageStuctureSdo Parse(Frame message) { const Byte MASK_COBEID = 0x7F; // Выделяет 7 бит содержащих CodeId из поля Id IncomingMessageStuctureSdo frame = new IncomingMessageStuctureSdo(); frame.Answer = message; frame.CobeId = (Byte)(((Byte)message.Identifier) & MASK_COBEID); // Длина данных ответа в SDO всегда 8 if (message.Data.Length == 8) { frame.DL = (DataLenght)message.Data[ANSWER_STUCTURE.DL]; switch (frame.DL) { case DataLenght.OneByte: { frame.Value = message.Data[ANSWER_STUCTURE.D0]; break; } case DataLenght.TwoBytes: { frame.Value = System.Convert.ToUInt32( (message.Data[ANSWER_STUCTURE.D0] | ((UInt32)message.Data[ANSWER_STUCTURE.D1] << 8))); break; } case DataLenght.ThreeBytes: { frame.Value = System.Convert.ToUInt32( (message.Data[ANSWER_STUCTURE.D0] | ((UInt32)message.Data[ANSWER_STUCTURE.D1] << 8) | ((UInt32)message.Data[ANSWER_STUCTURE.D2] << 16))); break; } case DataLenght.FourBytes: case DataLenght.Ecxeption: { frame.Value = System.Convert.ToUInt32( (message.Data[ANSWER_STUCTURE.D0] | ((UInt32)message.Data[ANSWER_STUCTURE.D1] << 8) | ((UInt32)message.Data[ANSWER_STUCTURE.D2] << 16) | ((UInt32)message.Data[ANSWER_STUCTURE.D3] << 32))); break; } case DataLenght.NotDefined: default: { frame._HasIncorrectStructure = true; frame.Value = 0; break; } } } else { frame._HasIncorrectStructure = true; frame.Value = 0; } if (!frame._HasIncorrectStructure) { frame.Index = message.Data[ANSWER_STUCTURE.INDEXLOW]; frame.Index = System.Convert.ToUInt16((frame.Index | (message.Data[ANSWER_STUCTURE.INDEXHIGH] << 8))); frame.SubIndex = message.Data[ANSWER_STUCTURE.SUBINDEX]; } else { frame.Index = 0; frame.SubIndex = 0; } return(frame); }
/// <summary> /// Обработать принятые сообщения /// </summary> /// <param name="messages">Входящие сообщения из сети</param> public override void HandleIncomingMessages(Frame[] messages) { String msg; IncomingMessageStuctureSdo msghelper; DeviceContext deviceContex; DataObject obj; // Индексы объектов которые не обрабатываем!!! List <UInt16> notHandledIndexes = new List <ushort>(); notHandledIndexes.AddRange(new UInt16[] { 0x2001, 0x2002 }); if (Status != Status.Running) { return; } foreach (Frame message in messages) { // Сообщение для этого сервиса. Получаем структуру сообщения msghelper = IncomingMessageStuctureSdo.Parse(message); if (!msghelper.IsForService) { // Сообещение не для этого сервиса continue; } // Ищем устройство с данным CobId. Если устройство найдено, // то устанавливаем флаг deviceContex = _Context.FindDevice(msghelper.CobeId); if (deviceContex == null) { // Устройство не найдено. msg = String.Format( "Network {0}: SDO Service: Получено сообщение от незарегистрированного устройства. " + "Message - {1}", _NetworkController.Description, message.ToString()); //Trace.TraceError(msg); continue; } // Устройство найдено. Проверяем его текущий запрос, т.е index if ((deviceContex.CurrentTransaction != null) && (deviceContex.CurrentTransaction.Status == TransactionStatus.Running)) { if (msghelper.HasExсeption) { if (GetIndex(deviceContex.CurrentTransaction.Request.Value) == msghelper.Index) { msg = String.Format( "Network {0}: SDO Service: Устройсво вернуло исключение {1}", _NetworkController.NetworkId, msghelper.ExceptionCode); deviceContex.CurrentTransaction.Abort(msghelper.Answer, msg); } else { msg = String.Format("Network {0}: SDO Service: " + "Индекс в ответе не соответствует запрошенному. Запрос - {1}, Ответ - {2}", NetworkController.NetworkId, deviceContex.CurrentTransaction.Request.Value.ToString(), message.ToString()); } continue; } // Проверяем индекс запроса и ответа if (GetIndex(deviceContex.CurrentTransaction.Request.Value) != msghelper.Index) { // Прищёл ответ на несуществующий запрос msg = String.Format("Network {0}: SDO Service: " + "Индекс в ответе не соответствует запрошенному. Запрос - {1}, Ответ - {2}", NetworkController.NetworkId, deviceContex.CurrentTransaction.Request.Value.ToString(), message.ToString()); //Trace.TraceError(msg); continue; } else { // Устанавливаем ответ на запрос //deviceContex.CurrentTransaction.Answer = frmSdo.Answer; // Проверяем контекст if (deviceContex.CurrentObject.Index != msghelper.Index) { msg = String.Format( "Network {0}: SDO Service: Индекс в ответе соответствует запрошенному {1}, " + "но не соотвествует индексу в контексте {2}", _NetworkController.NetworkId, msghelper.Index, deviceContex.CurrentObject); deviceContex.CurrentTransaction.Abort(msghelper.Answer, msg); throw new Exception(msg); } } // Всё нормально, subindex-ы не проверяем они всегда равны 0; // Исключений нет, устанавливаем новое полученное значение для объекта // с указанным индексом // Получаем тип данных соответствующего идекса для соотвествующего профиля устройства ObjectInfo objInfo; if (!deviceContex.Device.Profile.ObjectInfoList.Contains(msghelper.Index)) { msg = String.Format("Network {0}: SDO Service: " + "Ненайдено описание объекта c индексом {1} в профиле устройства {2}", _NetworkController.Description, msghelper.Index, deviceContex.Device.Profile.DeviceType); deviceContex.CurrentTransaction.Abort(msghelper.Answer, msg); throw new InvalidOperationException(msg); } else { objInfo = deviceContex.Device.Profile.ObjectInfoList[msghelper.Index]; } if (!deviceContex.Device.ObjectDictionary.Contains(msghelper.Index)) { msg = String.Format("Network {0}: SDO Service: " + "Ненайден объект c индексом {1} в словаре устройства {2}", _NetworkController.Description, msghelper.Index, deviceContex.Device.NodeId); deviceContex.CurrentTransaction.Abort(msghelper.Answer, msg); throw new InvalidOperationException(msg); } else { obj = deviceContex.Device.ObjectDictionary[msghelper.Index]; } // Проверяем модификатор доступа к значению. Если "только для чтения" // и тип: System или Configuration, то параметр конфигурационный. // В этом случае сравниваем прочитанное значение со значением объета словаря. // Если они не совпадают, устанавливаем статус "Ошибка конфигурации" // Если тип Measured, то читаем и устанавливаем данное значение из устройства if ((objInfo.ReadOnly) && (objInfo.Category == Category.Configuration || objInfo.Category == Category.System)) { if (notHandledIndexes.Contains(obj.Index)) { // Обновляем время последнего прочтения obj.Value = obj.Value; // Закрываем транзакцию deviceContex.CurrentTransaction.Stop(msghelper.Answer); continue; } if (msghelper.Value != obj.Value) { // Ошбика конфигурации msg = String.Format( "Ошбика конфигурации устройства: Устройство NodeId={0}, Индекс объекта={1}. " + "Значение в словаре={2}, прочитнное значение={3}", deviceContex.Device.NodeId, msghelper.Index, obj.Value, msghelper.Value); // Обновляем дату последнего обновления параметра obj.Value = obj.Value; // Устанавливаем код состояния объекта словаря obj.Status = ObjectStatus.ConfigurationError; // Записываем ошибку в лог // ... deviceContex.CurrentTransaction.Abort(msghelper.Answer, msg); } else { // Ошибки нет. // Обновляем дату последнего обновления параметра obj.Value = obj.Value; // Устанавливаем код состояния объекта словаря obj.Status = ObjectStatus.NoError; deviceContex.CurrentTransaction.Stop(msghelper.Answer); } } else { // Записываем новое значение в БД // TODO: реализовать проверку на допустимые значение объекта obj.Value = msghelper.Value; obj.Status = ObjectStatus.NoError; deviceContex.CurrentTransaction.Stop(msghelper.Answer); } } } }
/// <summary> /// Разбирает ответное сообщение /// </summary> /// <param name="message">Ответное сообщение</param> /// <returns>Структура данных ответа</returns> internal static IncomingMessageStuctureSdo Parse(Frame message) { const Byte MASK_COBEID = 0x7F; // Выделяет 7 бит содержащих CodeId из поля Id IncomingMessageStuctureSdo frame = new IncomingMessageStuctureSdo(); frame.Answer = message; frame.CobeId = (Byte)(((Byte)message.Identifier) & MASK_COBEID); // Длина данных ответа в SDO всегда 8 if (message.Data.Length == 8) { frame.DL = (DataLenght)message.Data[ANSWER_STUCTURE.DL]; switch (frame.DL) { case DataLenght.OneByte: { frame.Value = message.Data[ANSWER_STUCTURE.D0]; break; } case DataLenght.TwoBytes: { frame.Value = System.Convert.ToUInt32( (message.Data[ANSWER_STUCTURE.D0] | ((UInt32)message.Data[ANSWER_STUCTURE.D1] << 8))); break; } case DataLenght.ThreeBytes: { frame.Value = System.Convert.ToUInt32( (message.Data[ANSWER_STUCTURE.D0] | ((UInt32)message.Data[ANSWER_STUCTURE.D1] << 8) | ((UInt32)message.Data[ANSWER_STUCTURE.D2] << 16))); break; } case DataLenght.FourBytes: case DataLenght.Ecxeption: { frame.Value = System.Convert.ToUInt32( (message.Data[ANSWER_STUCTURE.D0] | ((UInt32)message.Data[ANSWER_STUCTURE.D1] << 8) | ((UInt32)message.Data[ANSWER_STUCTURE.D2] << 16) | ((UInt32)message.Data[ANSWER_STUCTURE.D3] << 32))); break; } case DataLenght.NotDefined: default: { frame._HasIncorrectStructure = true; frame.Value = 0; break; } } } else { frame._HasIncorrectStructure = true; frame.Value = 0; } if (!frame._HasIncorrectStructure) { frame.Index = message.Data[ANSWER_STUCTURE.INDEXLOW]; frame.Index = System.Convert.ToUInt16((frame.Index | (message.Data[ANSWER_STUCTURE.INDEXHIGH] << 8))); frame.SubIndex = message.Data[ANSWER_STUCTURE.SUBINDEX]; } else { frame.Index = 0; frame.SubIndex = 0; } return frame; }