Сообщество программистов Autodesk в СНГ

ADN Club => Civil 3D API => Тема начата: Захаров Максим от 02-11-2017, 22:22:46

Название: Записать объектные данные
Отправлено: Захаров Максим от 02-11-2017, 22:22:46
Здравствуйте.
Вопрос скорее не по Civil3d а по Map3d. Просто нет в этом форуме ветки Map3d.
Пытаюсь присвоить примитиву объектные данные (OD). Код взял с просторов интернета (http://adndevblog.typepad.com/infrastructure/2012/05/adding-object-data-records-to-entity-using-map-3d-api.html)
Сделал функцию под себя. Идея проста, таблица создана необходимо ее присвоить объекту и записать свои данные. Данные содержатся в двухмерном массиве (0-имя поля, 1-значение). Раз 200 функция в цикле нормально срабатывает, потом все вылетает со следующим сообщением (Необработанное исключение типа "System.AccessViolationException" в ManagedMapApi.dll). Может кто подскажет как с этим бороться.
Код функции здесь, но это аналог вышеперчисленной ссылки
Код - vb.net [Выбрать]
  1. Public Shared Function FuncAddOD2(ByVal DataArray(,) As String, ByVal tableOBJ As ObjectData.Table, ByVal objID As ObjectId) As Boolean
  2.         FuncAddOD2 = False
  3.         If IsArray(DataArray) = False Then Exit Function
  4.         Try
  5.             Dim odRecords As ObjectData.Records = tableOBJ.GetObjectTableRecords(Convert.ToInt32(0), objID, Constants.OpenMode.OpenForRead, False)
  6.             Dim odRecord As ObjectData.Record = odRecords.Item(0)
  7.             odRecord = Autodesk.Gis.Map.ObjectData.Record.Create()
  8.             tableOBJ.InitRecord(odRecord)
  9.             Dim mapVal As Autodesk.Gis.Map.Utilities.MapValue
  10.             'читаем поля таблицы
  11.             Dim recTable As Autodesk.Gis.Map.ObjectData.FieldDefinitions = tableOBJ.FieldDefinitions 'читаем поля
  12.             For i As Integer = 0 To recTable.Count - 1
  13.                 Dim FieldItem As FieldDefinition = recTable.Item(i)
  14.                 Dim NameField As String = FieldItem.Name 'имя поля
  15.                 mapVal = odRecord(i)
  16.                 Dim boolFlag As Boolean = False
  17.                 For k As Integer = 0 To DataArray.GetUpperBound(1)
  18.                     Dim NameFl As String = DataArray(0, k) 'имя поля из массива
  19.                     Dim NewVAl As String = DataArray(1, k) 'значение которое необходимо записать
  20.                     If NameFl Like NameField Then 'если названия полей совпадают заносим значение
  21.                         If FieldItem.Type = Constants.DataType.Integer Then 'если это целое число
  22.                             Dim IntRec As Integer = Val(NewVAl)
  23.                             mapVal.Assign(IntRec)
  24.                             Exit For
  25.                         ElseIf FieldItem.Type = Constants.DataType.Real Then
  26.                             Dim IntRec As Double = Val(NewVAl)
  27.                             mapVal.Assign(IntRec)
  28.                             Exit For
  29.                         ElseIf FieldItem.Type = Constants.DataType.Character Then
  30.                             Dim IntRec As String = Val(NewVAl)
  31.                             mapVal.Assign(IntRec)
  32.                             Exit For
  33.                         Else
  34.                             mapVal.Assign(NewVAl)
  35.                             Exit For
  36.                         End If
  37.                     End If
  38.                 Next k
  39.             Next i
  40.             tableOBJ.AddRecord(odRecord, objID)
  41.             odRecord.Dispose()
  42.             odRecords.Dispose()
  43.             FuncAddOD2 = True
  44.         Catch ex As MapException
  45.         Catch ex As System.AccessViolationException
  46.         End Try
  47.     End Function
Название: Re: Записать объектные данные
Отправлено: Александр Ривилис от 02-11-2017, 22:47:44
Для начала попробуй запустить оригинальную функцию в цикле > 200 раз. Если будет вылет, то скорее всего это баг.
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 03-11-2017, 10:15:32
Попробую конечно. Но вроде и так я полный аналог сделал.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 03-11-2017, 12:57:33
Непонятно. В строке 6 получаем первую запись из коллекции, а в строке 7 - создаём в эту же переменную новую запись. Зачем тогда получали запись? Если строка 6 лишняя, то и получать объект odRecords не имеет смысла. В исходном примере тоже этот непонятный момент присутствует.
Далее, вместо явного вызова Dispose лучше использовать конструкцию Using - так будет безопаснее.
Я пока не сильно вникал в суть кода, но, вроде как, получается, что при каждом вызове метода добавляется новая запись в таблицу (строки 7-8). 200 раз запись выполняется для одного объекта? Возможно, что есть какое-то ограничение по количеству записей.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 03-11-2017, 13:02:48
Данные содержатся в двухмерном массиве (0-имя поля, 1-значение).
А почему массив, а не словарь (Dictionary<string, string>)? Словарь гораздо удобнее - код более читаемый будет.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 03-11-2017, 13:27:05
Хм... А в строках 33-34 не погорячился? То есть, тип данных определить не удалось и выполняется запись "на авось"?
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 04-11-2017, 15:38:42
Спасибо за отклик Дмитрий.
Строки 6 и 7 полностью "слямзил" с примера.
Про Dispose - тоже сделал как в примере. А как должна выглядеть конструкция с Using?
Таблица данных (записи) добавляются для множества объектов. Суть программы чтение данных из xml. По данным xml сперва создается полилиния затем ей присваиваются OD данные. Если линий не очень много в файле то все ок, иначе ошибка. Может можно как то частями обрабатывать? Скажем обработать 200 полилиний, завершить программу и потом еще раз запустится, только конечно автоматически без участия пользователя? Полилинии создаются нормально, если отключить функцию записи OD данных все они рисуются.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 04-11-2017, 15:42:00
Я могу только на C# перевести и показать. В VB не силён. Устроит?
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 04-11-2017, 17:56:09
Давай, попробую.
Убрал 6 строку, вообще исключил строки c Dispose. Пока не помогает. Последнюю ветку условия Else то же убрал.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 06-11-2017, 12:53:28
Полилинии создаются нормально, если отключить функцию записи OD данных все они рисуются.
По моему опыту, это не всегда значит, что ошибка именно в этой части кода. Например, "за кадром" осталась работа с MapObjectData-таблицей. Как она открывается? Какое время она остаётся открытой? И т.п.
Вот примерно так я бы написал этот метод (проверять не на чем, поэтому, вообще не запускал):
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using Autodesk.Gis.Map;
  3. using Autodesk.Gis.Map.ObjectData;
  4. using Autodesk.Gis.Map.Utilities;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Globalization;
  8. using MapCon = Autodesk.Gis.Map.Constants;
  9. using MapOD = Autodesk.Gis.Map.ObjectData;
  10. using System.Linq;
  11.  
  12. namespace C3dTest
  13. {
  14.     class ObjectDataTest
  15.     {
  16.         public static bool AddObjectData(Dictionary<string, string> dataDict, MapOD.Table table, ObjectId objId)
  17.         {
  18.             if (dataDict == null || dataDict.Count == 0) return false;
  19.             try
  20.             {
  21.                 // Создаём новую запись (используем конструкцию using вместо прямого вызова Dispose)
  22.                 using (MapOD.Record newRec = Record.Create())
  23.                 {
  24.                     // Приводим запись в соответствие с таблицей                  
  25.                     table.InitRecord(newRec);
  26.  
  27.                     // Получаем коллекцию описаний полей таблицы
  28.                     MapOD.FieldDefinitions fieldDefs = table.FieldDefinitions;
  29.  
  30.                     // Проходим по описаниям полей таблицы
  31.                     for (int i = 0; i < fieldDefs.Count; i++)
  32.                     {
  33.                         // Текущее описание
  34.                         FieldDefinition fieldDef = fieldDefs[i];
  35.  
  36.                         // Имя текущего описания поля
  37.                         string fieldName = fieldDef.Name;                        
  38.  
  39.                         // Пробуем получить из словаря данные с ключом,
  40.                         // соответствующим названию поля в таблице
  41.                         KeyValuePair<string, string> dataKeyValue = dataDict
  42.                             .FirstOrDefault(item => item.Key.Equals
  43.                             (fieldName, StringComparison.InvariantCultureIgnoreCase));
  44.  
  45.                         // Если не нашли - переходим к следующему описанию поля
  46.                         if (string.IsNullOrEmpty(dataKeyValue.Key)) continue;
  47.  
  48.                         // значение, которое необходимо записать  
  49.                         string dataValue = dataKeyValue.Value;                                                        
  50.  
  51.                         // если тип поля - целое число
  52.                         if (fieldDef.Type == MapCon.DataType.Integer)
  53.                         {
  54.                             // и если данные приводятся к целому числу
  55.                             int value;
  56.                             if (Int32.TryParse
  57.                                 (dataValue,
  58.                                 NumberStyles.Integer,
  59.                                 CultureInfo.InvariantCulture, out value))
  60.                             {                              
  61.                                 // записываем приведённое значение
  62.                                 newRec[i].Assign(value);
  63.                             }                          
  64.                         }
  65.                         // если тип поля - вещественное число
  66.                         else if (fieldDef.Type == MapCon.DataType.Real)
  67.                         {
  68.                             // и данные приводятся к вещественному числу
  69.                             double value;
  70.                             if (double.TryParse
  71.                                 (dataValue,
  72.                                 NumberStyles.Number,
  73.                                 CultureInfo.InvariantCulture,
  74.                                 out value))
  75.                             {
  76.                                 // записываем приведённое значение
  77.                                 newRec[i].Assign(value);
  78.                             }                            
  79.                         }
  80.                         // если тип поля - строка
  81.                         else if (fieldDef.Type == MapCon.DataType.Character)
  82.                         {
  83.                             // записываем строку
  84.                             newRec[i].Assign(dataValue);                          
  85.                         }
  86.                         // другие варианты не рассматриваем
  87.                         else
  88.                         {                            
  89.                         }
  90.                     }
  91.                     // добавляем запись в таблицу
  92.                     table.AddRecord(newRec, objId);
  93.                 }
  94.                 return true;
  95.             }
  96.             catch (MapException ex)
  97.             {
  98.             }
  99.             catch (System.AccessViolationException ex)
  100.             {
  101.             }
  102.             return false;
  103.         }
  104.  
  105.         //=======================================================
  106.         //Service provided by Telerik (www.telerik.com)
  107.         //Conversion powered by NRefactory.
  108.         //Twitter: @telerik
  109.         //Facebook: facebook.com/telerik
  110.         //=======================================================
  111.     }
  112. }
  113.  
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 06-11-2017, 19:38:29
Спасибо Дмитрий. Начало работы с таблицей простое
Код - vb.net [Выбрать]
  1. Dim mapApp As MapApplication = HostMapApplicationServices.Application
  2.                                       Dim activeProject As Project.ProjectModel = mapApp.ActiveProject
  3.                                         Dim tableList As ObjectData.Tables = activeProject.ODTables 'забираем таблицы
  4.                                         Dim tableOBJ As ObjectData.Table = mapApp.ActiveProject.ODTables.Item("CADASTRAL_PARCEL")
Таблица уже существует. Только добавляй записи. Сегодня выявил один интересный момент. После каждого вызова функции (FuncAddOD2), добавил диалог MsgBox со счетчиком. Пришлось поработать клавиатурой, но чудо программа отработала примерно 1300 раз, без всяких ошибок. Может таймер какой добавить?
Название: Re: Записать объектные данные
Отправлено: Александр Ривилис от 06-11-2017, 20:25:15
Может таймер какой добавить?
Скорее не таймер, а вызов Application.DoEvents()
Название: Re: Записать объектные данные
Отправлено: trir от 07-11-2017, 08:47:29
А где acTrans.Commit();
Велосипед изобретаешь? (https://github.com/triroakenshield/RosReestrImpLib/blob/master/RosReestrImp/MyCommands.cs)
Название: Re: Записать объектные данные
Отправлено: Александр Ривилис от 07-11-2017, 13:35:57
Раз 200 функция в цикле нормально срабатывает, потом все вылетает со следующим сообщением (Необработанное исключение типа "System.AccessViolationException" в ManagedMapApi.dll).
Случайно не 255 (или 256) раз?
У меня такое ощущение, что в строке:
Код - vb.net [Выбрать]
  1. Dim odRecords As ObjectData.Records = tableOBJ.GetObjectTableRecords(Convert.ToInt32(0), objID, Constants.OpenMode.OpenForRead, False)
объект открывается, но всё время происходят какие-то исключения (а в catch нет диагностической печати) и
Код - vb.net [Выбрать]
  1. odRecords.Dispose()
не вызывается.
Объект можно открыть для чтения только 255 раз. В следующий раз будет ошибка. Поэтому нужно не забывать его закрывать. Можно попробовать в явном виде  вызвать odRecords.Dispose() сразу, как только он становится ненужен.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 07-11-2017, 13:46:30
Судя по коду, этот объект совершенно не нужен. Не знаю, зачем его получают как в этом коде, так и в примере на ADNDevBlog.
Название: Re: Записать объектные данные
Отправлено: Александр Ривилис от 07-11-2017, 14:00:44
Мне кажется, что этот пример актуальнее и точнее: http://jprdintprev.autodesk.com/adn/servlet/devnote?siteID=4814862&id=17435757&linkID=4900588
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 07-11-2017, 19:49:50
Исключил вообще строку с вызовом ObjectData.Records. Не помогло. Попробую другой пример (http://jprdintprev.autodesk.com/adn/servlet/devnote?siteID=4814862&id=17435757&linkID=4900588)
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 07-11-2017, 20:41:34
Немного переработал функцию, но только в части обработки массива, все остальное взял от ADN.
Код - vb.net [Выбрать]
  1. Public Shared Function FuncAddOD2(ByVal DataArray(,) As String, ByVal tableOBJ As ObjectData.Table, ByVal objID As ObjectId) As Boolean
  2.         FuncAddOD2 = False
  3.         If IsArray(DataArray) = False Then Exit Function
  4.         Using lk As DocumentLock = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument()
  5.             Try
  6.                 For k As Integer = 0 To DataArray.GetUpperBound(1)
  7.                     Dim NameFl As String = DataArray(0, k) 'имя поля из массива
  8.                     Dim NewVAl As String = DataArray(1, k) 'значение которое необходимо записать
  9.                     Using odRecords As ObjectData.Records = tableOBJ.GetObjectTableRecords(0, objID, Constants.OpenMode.OpenForWrite, True)
  10.                         'Dim odRecord As ObjectData.Record = odRecords.Item(0)
  11.                         Dim odRecord As ObjectData.Record = Autodesk.Gis.Map.ObjectData.Record.Create()
  12.                         tableOBJ.InitRecord(odRecord)
  13.                         tableOBJ.AddRecord(odRecord, objID)
  14.                     End Using
  15.                     Using odRecords As ObjectData.Records = tableOBJ.GetObjectTableRecords(0, objID, Constants.OpenMode.OpenForWrite, True)
  16.                         For Each rec As Record In odRecords
  17.                             For i As Integer = 0 To rec.Count - 1
  18.                                 Dim tblDefs As FieldDefinitions = tableOBJ.FieldDefinitions
  19.                                 Dim column As FieldDefinition = Nothing
  20.                                 column = tblDefs(i)
  21.                                 If NameFl Like column.Name Then
  22.                                     rec(i).Assign(NewVAl)
  23.                                     odRecords.UpdateRecord(rec)
  24.                                 End If
  25.                             Next i
  26.                         Next
  27.                     End Using
  28.                 Next k
  29.             Catch ex As MapException
  30.             Catch ex As System.AccessViolationException
  31.             End Try
  32.         End Using
  33.         FuncAddOD2 = True
  34.     End Function
Не помогает. Все тоже самое. На объеме 1300 объектов не работает. Вводишь MsgBox со счетчиком, и все нормально. Такое ощущение что что-то не успевает обновлятся
Название: Re: Записать объектные данные
Отправлено: Александр Ривилис от 07-11-2017, 20:44:00
            Catch ex As MapException
            Catch ex As System.AccessViolationException
            End Try
Вставь здесь печать отладочной информации. Хочу убедиться, что нет исключений.
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 07-11-2017, 21:14:37
Попробуй без транзакций открывать таблицу и объекты.
Вообще, ещё раз обращаю внимание, что проблема может быть не в этом методе.
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 07-11-2017, 21:55:05
А как это сделать? Информация не выводится, либо фатальная ошибка либо вот это (это в режиме отладки). Проблема все таки наверное в этой функции потому что я повторяю если ввести в код MSgBox и нажать Enter 1300 раз то программа дорабатывает до конца, как надо. Транзакция закрыта. Хотя перехват ошибок введен
Название: Re: Записать объектные данные
Отправлено: trir от 08-11-2017, 06:35:48
почему у меня никаких проблем не возникает
Код - C# [Выбрать]
  1.         public void AddAttr(ObjectId wid, Data.MyRecord wr, Autodesk.Gis.Map.ObjectData.Table wTbl)
  2.         {
  3.             AppServ.Document acDoc = AppServ.Application.DocumentManager.MdiActiveDocument;
  4.             using (AppServ.DocumentLock acLckDoc = acDoc.LockDocument())
  5.             {
  6.                 using (Transaction acTrans = acDoc.Database.TransactionManager.StartTransaction())
  7.                 {
  8.                     using (Record odRecord = Record.Create())
  9.                     {
  10.                         wTbl.InitRecord(odRecord);
  11.                         FieldDefinition fdef;
  12.                         Data.FieldValue fval;
  13.                         for (int i = 0; i < wTbl.FieldDefinitions.Count; i++)
  14.                         {
  15.                             fdef = wTbl.FieldDefinitions[i];
  16.                             fval = wr.SearchField(fdef.Name);
  17.                             if (fval != null)
  18.                             {
  19.                                 if (!fval.IsGeom)
  20.                                 {
  21.                                     odRecord[i].Assign(fval.GetString());
  22.                                 }
  23.                             }
  24.                         }
  25.                         wTbl.AddRecord(odRecord, wid);
  26.                         acTrans.Commit();
  27.                     }                    
  28.                 }
  29.             }
  30.         }
1758 записей - полёт нормальный
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 08-11-2017, 08:56:24
А как Data.MyRecord wr получаем? Можно по подробнее, может мне действительно не стоит массивом пользоваться?
Название: Re: Записать объектные данные
Отправлено: trir от 08-11-2017, 08:58:31
я уже приводил ссылку на github (https://github.com/triroakenshield/RosReestrImpLib/tree/master/RosReestrImp)
но дело не в массиве
Название: Re: Записать объектные данные
Отправлено: Дмитрий Загорулькин от 08-11-2017, 13:34:44
Возможно, причина в том, что у trir за одну транзакцию добавляется только одна запись, а не 1000 сразу.
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 08-11-2017, 14:10:49
Спасибо. Буду пробовать. Надеюсь trir добавлял несколько записей в один примитив, а не одну запись в один примитив
Название: Re: Записать объектные данные
Отправлено: trir от 08-11-2017, 14:12:17
Цитировать
добавлял несколько записей в один примитив
зачем?
Название: Re: Записать объектные данные
Отправлено: Захаров Максим от 08-11-2017, 20:57:06
Всем спасибо. Вроде пока все работает. Натолкнула идея trir и Дмитрия добавлять по одной записи в примитив, а не все сразу. Правда я использовал образец от ADN который Александр Ривилис подсказал.
А может я строки
Код - vb.net [Выбрать]
  1.  Dim mapApp As MapApplication = HostMapApplicationServices.Application
  2.  Dim activeProject As Project.ProjectModel = mapApp.ActiveProject
  3.  Dim tableList As ObjectData.Tables = activeProject.ODTables 'забираем таблицы
  4.  Dim tableOBJ As ObjectData.Table = mapApp.ActiveProject.ODTables.Item("CADASTRAL_PARCEL")
за пределы цикла вывел.
Раньше у меня было так создавался по координатам примитив (полилиния) затем открывалась таблица (4 строка). В любом случае по эксперементирую. А результат такой (это к вопросу зачем в один примитив несколько записей добавлять). Значения полей будут конечно же другими.

(https://s1.postimg.org/9mq37vbh4b/image.jpg) (https://postimg.org/image/9mq37vbh4b/)
Название: Re: Записать объектные данные
Отправлено: trir от 09-11-2017, 04:37:18
Цитировать
А результат такой (это к вопросу зачем в один примитив несколько записей добавлять).
это одна запись
ты путаешь запись и поля