пятница, 20 июля 2012 г.

Чтение файла SolidWorks


В продолжение предыдущих статей про SolidWorks...
P.S.:  В предыдущих статьях я описывал как можно просмотреть иерархию файла и пытался извлечь дерево материалов изделия. Для получения дерева материалов я использовал неверный подход.

Задача: прочитать фаил сохранений SolidWorks и получить информацию с описанием изделия и деревом материалов.

Для реализации я использую
Исходный код примеров можно получить/просмотреть тут GitHub


Данный пост я разобью на 3 шага:
  1. Чтение файла, поиск доступных данных
  2. Получение необходимых данных
  3. Получение дерева материалов в формате XML


Шаг 1. Чтение файла, поиск доступных данных
Для чтение файла я использую такой код
FileInputStream fis = new FileInputStream(new File(path));
POIFSFileSystem poifsFileSystem = new POIFSFileSystem(fis);
fis.close();
return poifsFileSystem.getRoot();
В результате чего я получаю Root каталог внутренней структуры файла, как правило это объект DirectoryNode. В нутрии фаил содержит иерархию в которой хранится информация по изделию
Дерево в внутри файла состоит из DirectoryNode и DocumentNode объектов.

После получения корневого элемента, нам необходимо просмотреть иерархую, дабы узнать где искать необходимою информацию. Прочитать иерархию можно с помощью данного кода:
public void readNodeAndWriteToConsoleContains(DirectoryNode node) {
        Iterator iterator = node.getEntries();
        readIterator(iterator);
    }
private void readIterator(Iterator iterator) {
        while (iterator.hasNext()) {
            readNode(iterator.next());
        }
    }
private void readNode(Entry node) {
        if (node.isDocumentEntry()) {
            System.out.println(" == Directory name: " + node.getParent().getName() + " -- Document: " + node.getName());
        }
        if (node.isDirectoryEntry()) {
            System.out.println("");
            System.out.println(" == Directory name: " + node.getName());
            readIterator(((DirectoryNode) node).getEntries());
            System.out.println("");
        }
    }


В результате работы кода мы получим что то подобное:
 == Directory name: Root Entry -- Document: Config-0-Properties
 == Directory name: Root Entry -- Document: ISolidWorksInformation
 == Directory name: Root Entry -- Document: DocumentSummaryInformation
 == Directory name: swXmlContents
 == Directory name: swXmlContents -- Document: MATERIALTREE
 == Directory name: swXmlContents -- Document: KeyWords__ZLB
 == Directory name: swXmlContents -- Document: Tables__ZLB
Как правило вся вспомогательная информация по изделию размещается в  DocumentSummaryInformation  и SummaryInformation. 


Шаг 2. Получение необходимых данных
Для начала нам необходимо получить основную информацию, в моем случае это:
  • Наименование;
  • Обозначение;
  • Инвентарный номер документа.

Поля подобные SummaryInformation в нутрии себя хранят лист PropertySet в котором есть вся необходимая информация.
PropertySet summaryInformation = readDocumentNode.read(poifsFileSystem,                   DocumentSummaryInformation.DEFAULT_STREAM_NAME);
в нутрии метод read выглядит так:
public PropertySet read(POIFSFileSystem poifsFileSystem, String nameDocument) throws IOException {
        PropertySet ps = null;
        DocumentInputStream dis = poifsFileSystem.createDocumentInputStream(nameDocument);
        try {
            ps = new PropertySet(dis);
        } catch (NoPropertySetStreamException e) {
            e.printStackTrace();
        } catch (MarkUnsupportedException e) {
            e.printStackTrace();
        } finally {
            dis.close();
        }
        return ps;
    }
читаем PropertySet:
public List read(PropertySet propertySet, String docName) {
        List result = new ArrayList();
        PropertyEntry propertyEntry;
        for (Section section : propertySet.getSections()) {
            Map objects = section.getDictionary();
            Property[] properties = section.getProperties();
            Property property;
            for (int i = 0; i < objects.size(); i++) {
                property = properties[i];
                propertyEntry = new PropertyEntry();
                propertyEntry.setKey(objects.get(property.getID()));
                propertyEntry.setValue(property.getValue().toString());
                propertyEntry.setIndex(i);
                propertyEntry.setType(property.getType());
                propertyEntry.setDocumentName(docName);
                result.add(propertyEntry);
            }
        }
        return result;
    }
 
в результате чего получаем примерно следующие:
DocumentSummaryInformation / Наименование / Какойто чертеж
DocumentSummaryInformation / Обозначение / 87514567
DocumentSummaryInformation / Инвентарный номер документа / 221673
DocumentSummaryInformation / Формат / А4
... прочие данные ...  

Шаг 3. Получение дерева материалов в формате XML
Замечательно, мы получили все нужные нам данные, теперь для полноты данных, необходимо получить дерево материалов из которых состоит изделие.  Для этого мне надо в Директории файла swXmlContents, прочитать документ MATERIALTREE, данные в котором лежат в формате xml. Задачка простая, делаю это с помощью данного кода:
получаю корневой элемент дерева и перехожу в swXmlContents:
DirectoryNode swXmlContentsDir = (DirectoryNode) poifsFileSystem.getRoot().getEntry("swXmlContents");
после чего читаю документ  MATERIALTREE:
DocumentInputStream dis = readDocumentNode.getDocumentInputStream(swXmlContentsDir, "MATERIALTREE");
 SAXReader reader = new SAXReader();
 Document document = reader.read(dis);
getDocumentInputStream возвращает имплементацию InputStream из которой я могу прочитать поток данных, в моем случаем это данные в формате XML который я сразу читаю dom4j.




Заключение:

Спасибо за помощь Streamdown, пост в блоге "Необычная работа с документами solidworks".
Документация и примеры по работе с Apache POI

воскресенье, 15 июля 2012 г.

Чтение структуры файла SolidWorks 2007


Простецкий алгоритм для чтения структуры файла SolidWorks 2007


private void moveIter(Iterator iterator, String name) {
        while (iterator.hasNext()) {
            def it = iterator.next()
            if (it instanceof DocumentNode) {
                readDocumentNode(it, name)
            }
            if (it instanceof DirectoryNode) {
                DirectoryNode dn = (DirectoryNode) it
                String path = createPath(dn)
                moveIter(dn.entries, path)
            }
        }
    } 
private def readDocumentNode(DocumentNode documentNode, String path) {
        log.error('==-' + documentNode.name + path)
    }
private String createPath(DirectoryEntry directoryEntry) {
        String result = ''
        if (directoryEntry.parent) {
            result = createPath(directoryEntry.parent)
        }
        result = result + '/' + directoryEntry.name
        result
   }

В результате чего я получил такой список:


  • \005 DocumentSummaryInformation
  • \005SummaryInformation
  • Config-0-Properties
  • Header2
  • ISolidWorksInformation
  • ModelStamps
  • Preview
  • PreviewPNG

директория Contents
  • Config-0
  • CMgrHdr2
  • eModelLic
  • Config-0-GhostPartition
  • CnfgObjs
  • CMgr
  • OleItems
  • Config-0-LWDATA
  • Config-0-Partition
  • DisplayLists__ZLB
  • Definition

директория ThirdPty
  • MOLDFLOWXpress
  • Pw2StorageStream.1.35
  • Intermech_internal_structure
  • CM_MOTION_LOAD_1
  • Intermech_document_settings
  • CMMotionLoadMapU
  • PBPDFlt
  • SWA_Schedules
  • Animator__ZLB

директория ThirdPtyStore
  • VisualStates__ZLB
  • VisualStates_DisplayPane

директория _DL_VERSION_4400
  • DLUpdateStamp
  • History 
  • Biography 
  • AssyVisualData

директория swXmlContents
  • Tables__ZLB
  • KeyWords__ZLB
  • MATERIALTREE

Интересно, что в файле есть такие поля как Intermech, видимо они остаются если у вас в SolideWorks-е подключен плагин Intermech 

суббота, 14 июля 2012 г.

Разбор файла чертежа из SolidWorks 2007 и выше (заметки по MATERIALTREE)

Доброго времени суток.

Данный подход был не верен, так как выполняя код:
POIFSDocument doc = new POIFSDocument("MATERIALTREE", new FileInputStream(file))
я всегда получал сам файл, по это причине я его не мог раскрыть.


Чтение дерева материалов из файла описано в данном посте Чтение файла SolidWorks


Старая версия поста:
Заказчик поставил задачу  получать информацию из загруженных на сервер файлов чертежей SolidWorks.


Сделал предварительный обзор файлов SolidWorks, я выявил что они файлы 
базируются на OLE2.  Для работы с данным форматом файлов я использую http://poi.apache.org/

На сайте poi apache есть  простые примерчы как можно порчитать иформацию из файла.
При изучении содержимого файла, я натснулся на поле с именем "MATERIALTREE", мне зажотелось получить данные из этого поля, но тут то и были грабли....

Для начала, я не смог обратится простым способом так как DocumentInputStream и PropertySet мне отвечали NoPropertySetStreamException. Я нащел альтернативный подход к данному свойству: 
POIFSDocument doc = new POIFSDocument("MATERIALTREE", new FileInputStream(file))
 В результате мы получаем объект POIFSDocument который в себе содержет HexDump.
При разборе данного Hex-а:



        Object[] objs = doc.getViewableArray()
        Hex.decode(objs[0].toString())


Я получил примерно следующие:
00000000 D0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00 ................
00000010 00 00 00 00 00 00 00 00 3E 00 03 00 FE FF 09 00 ........>.......
00000020 06 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 ................
00000030 02 00 00 00 00 00 00 00 00 10 00 00 58 01 00 00 ............X...
00000040 03 00 00 00 FE FF FF FF 00 00 00 00 03 00 00 00
Потратив на разбор некоторое время, я наткнулся на сайт http://open-file.ru/, где по hex заголовку удалось выяснить данный hex может относится к :


ФорматТипОписание
.fpxРастровые изображенияФайл изображения FlashPix
HEXD0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00
.p65ИзображенияДокумент PageMaker 6.5
HEXD0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00
.pm6Размеченные документыДокумент PageMaker 6.0
HEXD0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00
.qxИсходный кодИсходный код Quexal
HEXD0 CF 11 E0 A1 B1 1A E1 00 00 00 00 00 00 00 00


....




пока это все наблюдения по разбору POIFSDocument "MATERIALTREE"
to be continued...