segunda-feira, 21 de janeiro de 2019

Extraindo Informações de documentos AutoCAD baseado em proximidade


Essa semana comecei um trabalho novo de verificação. Basicamente trata-se de verificar uma lista de construção. Essa lista é uma planilha em Excel que resume uma série de informações que também estão disponíveis em documentos DWG.

Inicialmente pensei em fazer uma análise qualitativa por amostragem. Nesse caso pegaria aleatoriamente parte das informações e se estivessem válidas assumiria que tudo estava correto. Talvez isso não seria o bastante para evitar um erro de engenharia afinal são 111 documentos DWG mais de 4000 informações para compatibilizar.

Pensando melhor é possível fazer um programa para extrair as informações do AutoCAD de modo sistemático?
Bom é possível sim e depende muito de como você vai isolar a informação.
A primeira parte do trabalho foi identificar o que estava padronizado no meio da quantidade de objetos e textos espalhados pelo documento.


Então vendo o texto padrão consegui notar duas coisas:

Todas as informações do texto estão alinhadas;

Existem pedaços de texto chaves como “VPtmin=” e “VPvext=”.

A ideia é localizar um objeto texto que contenha “VPtmin=”, como é um objeto vetorial tem coordenada de inserção podemos localizar sua coordenada X,Y e para textos próximos podemos associar como pertencentes ao mesmo “bloco” de informação.
Organizacionalmente o código seria assim:
  1. Listar todos os objetos do desenho; 
  2. Localizar os que são texto; 
  3. Localizar os que contêm o texto “VPtmin=”; 
  4. Listar novamente todos os objetos do desenho; 
  5. Localizar os que são texto; 

Se o texto tem coordenadas X,Y próximas do texto localizado em 3 associa (coloca na lista) incluindo o “VPtmin=”;

Aqui segue o exemplo de código

/// <summary>
/// Lê as informações de torre de diversos arquivos DWG de Planta e Perfil
/// e apresneta o resultado como uma lista em texto
/// </summary>
[CommandMethod("Extract1", CommandFlags.Session)]
public void Extract1()
{
    Document doc = Application.DocumentManager.MdiActiveDocument;
    Editor edo = doc.Editor;

    //Cria um texto de saída de dados
    String txtOuput = "";
  

    //Seleciona todos os objetos do DWG
    Database db = doc.Database;
    PromptSelectionResult acSSPrompt = Application.DocumentManager.MdiActiveDocument.Editor.SelectAll();

    //Verifica se a seleção foi OK
    if (acSSPrompt.Status != PromptStatus.OK)
    {
edo.WriteMessage("\nErro na seleção de objetos do comando PromptStatus = " + acSSPrompt.Status.ToString());
return;
    }

    //Abre a transaction
    Transaction tr = db.TransactionManager.StartTransaction();
    using (tr)
    {

SelectionSet acSSet = acSSPrompt.Value;

//Localiza todos os objetos que tem o texto padrão
foreach (SelectedObject acSSObj in acSSet)
{
    if (acSSObj == null)
continue;

    Entity acEnt = tr.GetObject(acSSObj.ObjectId, OpenMode.ForRead) as Entity;

    if (acEnt == null)
continue;

    // verifica se o objeto é um DBText, se sim, modifica o tamanho e o pontos de aprisionamento do texto.
    if ((acEnt.GetType() == typeof(Autodesk.AutoCAD.DatabaseServices.DBText)))
    {
DBText myText = new DBText();
myText = (DBText)acEnt;

if (myText.TextString.Contains("VPtmin="))
{
    //Dentro da mesma seleção busca os objetos que estão na proximidade
    foreach (SelectedObject acSSObj2 in acSSet)
    {
// Check to make sure a valid SelectedObject object was returned
if (acSSObj2 == null)
    continue;

// Open the selected object for write
Entity acEnt2 = tr.GetObject(acSSObj2.ObjectId, OpenMode.ForRead) as Entity;

// Check to make sure a valid Entity object was returned
if (acEnt2 == null)
    continue;

if ((acEnt2.GetType() == typeof(Autodesk.AutoCAD.DatabaseServices.DBText)))
{
    DBText myText2 = new DBText();
    myText2 = (DBText)acEnt2;
    if ((myText2.Position.X < (myText.Position.X + 5)) && (myText2.Position.X > (myText.Position.X - 5)))
    {
if ((myText2.Position.Y < (myText.Position.Y + 250)) && (myText2.Position.Y > (myText.Position.Y - 250)))
{
    //Se extá no range gera a saída.
    txtOuput += myText2.TextString + "\t";
}
    }
}
    }
    txtOuput += "\n";
}
    }
}
    }

    edo.WriteMessage("\nComando executado com sucesso!");

    //Apresenta em um formulário do windows com uma caixa de texto
    TextoutputForm tofrm = new TextoutputForm();
    tofrm.richTextBox1.Text = txtOuput;
    tofrm.Show();
}

Esse código em si tem alguns problemas.

Como ele só localiza o texto ele não organiza as informações, é possível que as informações venham embaralhadas o que daria um trabalho de reorganiza-las. No meu caso eu descobri que simplesmente elas já tinham sido inseridas em ordem então não gera grandes problemas.

Outro problema é que algum texto estiver duplicado ele aparecerá duas vezes por isso antes de executar é recomendável usar o comando OverKill para matar as duplicatas.
Por fim as informações próximas em formato de texto também serão importadas por isso é sempre necessário fazer uma limpeza e um ajuste nos ranges de busca dos textos próximos.

Com essa ferramenta acho que economizei mais de 1 semana de trabalho.

Esse é mais um exemplo de como um código relativamente simples pode simplificar muito o trabalho repetitivo no AutoCAD. As vezes vemos as informações soltas e sem ordem e pensamos que não é possível automatizar o processo mas pensando um pouco melhor isso é possível sim.

quinta-feira, 3 de janeiro de 2019

AutoCAD para JPEG/PNG/PS/BMP, como fazer isso rápido?

Como utilizar o Raster para fazer aquelas capturas rápidas do AutoCAD. Seja para colar no e-mail ou enviar no Skype sempre precisa mudar do AutoCAD para uma imagem. As duas formas são mais comuns são copiando e colando ou usando a captura de tela do Windows.

Como sempre, a Autodesk já pensou nisso. Existe uma ferramenta oculta no AutoCAD para isso.

Existem 5 comandos!


Existem cinco comandos pouco conhecidos que podem salvar seu dia e, quando você aprende como usar um, basicamente aprendeu todos eles.


JPGOUT permite exportar rapidamente um arquivo JPG. Primeiro, basicamente é solicitado informar um local e, em seguida, será solicitado a selecionar objetos ou exportar todos os objetos e viewports. É isso aí! Fim...



Três dos outros comandos funcionam da mesma maneira:


1.  PNGOUT (arquivo PNG ou Portable Network Graphics)
2. TIFOUT (TIFF ou Tagged Image File Format)
3. BMPOUT (BMP - um arquivo de bitmap independente de dispositivo)

A entrada final neste conjunto de funções de exportação de varredura é PSOUT. Cria um EPS, ou Arquivo PostScript Encapsulado. Isso é um levemente diferente. Como não há opção para selecionar objetos com PSOUT, ele cria um arquivo PostScript a partir do desenho. Arquivo vetorial que nem o AutoCAD, pouca gente usa o PostScript.



Esses comandos são mais antigos que andar para frente, mas pouca gente sabe da existência deles. E se você é alguém que precisa produzir esses arquivos, seja com frequência ou ocasionalmente certamente vai gostar da possibilidade de obter sua Raster mais rapidamente.

quinta-feira, 13 de dezembro de 2018

Obtendo informações do AutoCAD com DATAEXTRACTION

Essa semana tive que fazer um serviço de reunir informações de progressiva de obstáculos. Trata-se de um documento que contem um desenho em planta com todas as informações de obstáculos que uma dada linha de transmissão. Meu trabalho era classificá-los e definir um trecho em que não se pode construir ou deve ser evitada a construção de uma torre.

Como os objetos estão numa sequência como uma longa estrada organizada em quilômetros resolvi desenhar linhas continuas com o comando XLine no inicio e fim de cada obstáculo.


Como podem ver na foto cada obstaculo tem um XLine antes e depois do obstaculo. isso me dá duas quilometragens no eixo X de inicio e fim do obstaculo. Agora, como extrair essa informação do AutoCAD? Obter todas as coordenadas X das Xline's?

Como tudo no AutoCAD, isso já foi pensado e implementado. Existe um comando chamado DATAEXTRACTIONque permite que você exporte para uma planilha em Excel todas as informações que quiser a respeito dos objetos de um desenho.

Utilizando o DATAEXTRACTION

O DATAEXTRACTION é um comando universal para extrair informações, assim que você executar o comando aparecerá um wizard de 6 passos para você extrair as informações.

1° Passo - Criar ou chamar o arquivo de extração

O primeiro passo é criar um template, eu recomendo você sempre gravar um arquivo de extração customizado assim você poderá nas próximas vezes apenas utilizar a configuração de extração predefinida.

Se você já tiver um template pronto basta apenas chama-lo nessa janela e apertar NEXT.


2° Passo - Selecionar os desenhos e Objetos

Você pode inclusive selecionar mais de um documento DWG para obter informações ou ainda apenas uma região do documento. Por exemplo, se você está querendo contabilizar todos os blocos tomada de uma sala, poderia extrair só da região em questão.

No meu caso será todo o documento mesmo e apenas essa documento.


3° Selecionando os objetos

Nesta etapa você seleciona os objetos que serão necessários para extração, no meu caso será apenas os objetos do tipo XLine.

4° Selecione as propriedades

Neste passo você seleciona as propriedades dos objetos que você selecionou que vão ser listados no documento final. No meu caso selecionarei só o ponto de inserção X,Y,Z. A principio a unica informação que eu preciso mesmo é o X mas colocarei todas as três.

5° Pré-visualização dos dados

Esse quinto passo é apenas uma pré visualização dos dados que foram extraídos.


6° Exportar para aquivo

Você pode exportar para um arquivo de texto, xls, csv ou até mesmo um arquivo de dados do Microsoft Access. Ainda sim é possível montar uma tabela no próprio arquivo DWG.

No meu caso vou gerar um arquivo do Excel.


Pronto agora é só gerar a planilha de saída


Aqui está o resultado final:


Espero ter ajudado.



sexta-feira, 7 de dezembro de 2018

MOVEBAK - Salvando os *.bak's em outro local

Hoje estive em uma discussão com o pessoal de TI sobre e excesso de espaço consumido na rede por arquivos do tipo *.bak pela rede.
Para quem não sabe ou nunca percebeu sempre que você abre e edita um arquivo DWG automaticamente o AutoCAD gera na pasta onde está trabalhando com o documento uma cópia do desenho original que você está trabalhando.
A principio isso é ótimo, uma dica legal é que você pode simplesmente trocar a extensão de *.bak para *.dwg e voltar com o documento na versão antiga. Como o *.bak só é criado quando você abre e faz a primeira edição no arquivo principal se o AutoCAD travar, corromper o arquivo principal o arquivo de backup permanece inalterado.
Por outro lado uma cópia direta to *.deg implica em um arquivo do mesmo tamanho se você trabalha com milhares de documentos em um servidor de arquivos isso se torna um problema. 
Fiz uma busca no servidor da empresa e só de arquivos de backup do AutoCAD são mais de 200 Gb. Ou seja, só em cópias de arquivos temos um consumo de espaço enorme. Outro ponto é que ao abrir um arquivo na rede e o AutoCAD criar uma cópia dele na rede provoca uma latência grande e uso adicional do servidor, naturalmente a estação de trabalho (vulgo PC) faz o "download" momentâneo do arquivo da rede depois faz o upload para rede de uma cópia na pasta.
Outro ponto contra é que geralmente outros programas também usam a extensão *.bak para criação de arquivos de backup o que pode levar um desavisado a fazer uma busca total e deletar permanentemente todos os arquivos *.bak do AutoCAD e de outros softwares

A solução - MOVEBAK

A solução para isso está no comando MOVEBAK. Este comando muda a pasta de escrita dos backup's para uma pasta fixa ao invés de gravar sempre na pasta do documento aberto.
Melhor ainda se essa pasta for a pasta temporária do Windows. Essa pasta temporária do Windows conhecida como %temp% é limpa sempre pelos dispositivos de limpeza do computador e quando a pouco espaço. Para saber o endereço dessa pasta basta abrir o explorador de arquivos e digitar na linha do caminho %temp% e apertar enter. Será apresentado o caminho absoluto para utilizar no AutoCAD

Veja que qualquer caminho pode ser utilizado para salvar os backup essa pasta temporária do windows é uma sugestão minha

Para atualizar o caminho de salvamento dos backups no AutoCAD com qualquer documento aberto digite o comando MOVEBAK e cole o caminho onde deseja salvar no meu caso "C:\Users\**seu_usuário**\AppData\Local\Temp".

quarta-feira, 28 de novembro de 2018

AutoCAD .NET: Criando Seu Próprio Template em Visual Studio

Eu trabalho numa empresa não muito grande mas que não permite que as pessoas instalem aplicativos nos computadores, assim, não posso instalar nada sobre o Visual Studio.

Isso é um problema porque a Autodesk fornece o AutoCAD .NET Wizards que é um instalador de Templates já prontos para iniciar aplicações em .NET para AutoCAD.

Como não posso instalar me veio a ideia de montar meu próprio template.

1. Criando um projeto novo

O primeiro passo é criar um projeto novo, neste caso criei um projeto no Visual Studio 2013 em .NET Framework 4 porque estou trabalhando com o AutoCAD 2014.


2. Adicionando as referências

Para começar a desenvolver o projeto teremos que primeiro adicionar as Referências do AutoCAD .NET que estão no pacote Object ARX que você já deve ter feito DOWNLOAD no site da Autodesk.

O mínimo que você deve adicionar ao seu projeto são as seguintes dll's: "AcTcMgd.dll" "AcCoreMgd.dll" "AcDbMgd.dll" "AcMgd.dll" que estão no caminho: C:\Autodesk\Autodesk_ObjectARX_2014\inc


É importante que todas as DLL carregadas estejam na condição Copy Local em False. Isso faz com que o AutoCAD se mantenha usando as DLL que já estão carregadas nele.


3. Criando a estrutura mínima

O template padrão possuí pelo menos duas classes: 
1. Classe de Comandos: Onde serão carregados todos os comandos que serão feitos nessa DLL para o AutoCAD
2. Classe Plug-in de Inicialização e Terminação do processo atribuído a classe.

Então, dentro do projeto teremos que criar essas duas classes.

Como criei a partir de um projeto de DLL veio uma classe chamada Class1.cs, renomeei para Plugin.cs. Poderia ter colocado outro nome o que vai torna-lo o necessário para carregar uma DLL é o código abaixo:

using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AutoCAD_2014_Class_Library
{
    // This class is instantiated by AutoCAD once and kept alive for the
    // duration of the session. If you don't do any one time initialization
    // then you should remove this class.
    public class Plugin : IExtensionApplication
    {
        void IExtensionApplication.Initialize()
        {
            // Add one time initialization here
            // One common scenario is to setup a callback function here that
            // unmanaged code can call.
            // To do this:
            // 1. Export a function from unmanaged code that takes a function
            //    pointer and stores the passed in value in a global variable.
            // 2. Call this exported function in this function passing delegate.
            // 3. When unmanaged code needs the services of this managed module
            //    you simply call acrxLoadApp() and by the time acrxLoadApp
            //    returns  global function pointer is initialized to point to
            //    the C# delegate.
            // For more info see:
            // http://msdn2.microsoft.com/en-US/library/5zwkzwf4(VS.80).aspx
            // http://msdn2.microsoft.com/en-us/library/44ey4b32(VS.80).aspx
            // http://msdn2.microsoft.com/en-US/library/7esfatk4.aspx
            // as well as some of the existing AutoCAD managed apps.

            // Initialize your plug-in application here
                     

        }

        void IExtensionApplication.Terminate()
        {
            // Do plug-in application clean up here
        }
    }

}

Nesse código já se utiliza o Autodesk.AutoCAD.Runtime para criar a classe de "gerenciamento de inicialização e terminação" que é do tipo unico IExtensionApplication.

Essa classe mandatória tem que ter essas duas funções com retorno nulo (void):

IExtensionApplication.Initialize() função que permite que você inicialize a classe. Geralmente nos meus códigos eu inicializo um log de eventos nesse ponto para que conseguir depurar o que algum usuário fez.
IExtensionApplication.Terminate() função de finalização da classe, nesse ponto eu escrevo os logs e fecho os TextWrite object's.


O próximo passo é criar a classe de comandos.
Essa classe também precisa do using Autodesk.AutoCAD.Runtime
Então ficamos com o código assim:

using System;
using Autodesk.AutoCAD.Runtime;

// This line is not mandatory, but improves loading performances
[assembly: CommandClass(typeof(AutoCAD_2014_Class_Library.Comandos))]


namespace AutoCAD_2014_Class_Library
{
    // This class is instantiated by AutoCAD for each document when
    // a command is called by the user the first time in the context
    // of a given document. In other words, non static data in this class
    // is implicitly per-document!
    public class Comandos
    {
        /// <summary>
        /// Mostra o log de eventos da função do Plug-in.
        /// </summary>       
        [CommandMethod("comando_teste", CommandFlags.Modal)]
        public void comando_de_teste() // This method can have any name
        {
            System.Windows.Forms.MessageBox.Show("Isso é Um teste!", "Meu Template");
        }
    }

}

Esse código tem essa linha bizarra [assemblyCommandClass(typeof(AutoCAD_2014_Class_Library.Comandos))] que segundo a Autodesk melhora a performance dos comandos.

De resto é o que você já está acostumado, uma classe Comandos com um comando de teste que se chamará no prompt por comando_teste.


Estamos prontos para lançar a aplicação agora vamos configurar como chamar o AutoCAD.

3. Modificando o AssemblyInfo

Para carregar a DLL corretamente é necessário adicionar uma informação no AssemblyInfo

// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.//// Use the attributes below to control which key is used for signing.
//// Notes:
//   (*) If no key is specified, the assembly is not signed.//   (*) KeyName refers to a key that has been installed in the Crypto Service//       Provider (CSP) on your machine. KeyFile refers to a file which contains//       a key.//   (*) If the KeyFile and the KeyName values are both specified, the
//       following processing occurs://       (1) If the KeyName can be found in the CSP, that key is used.//       (2) If the KeyName does not exist and the KeyFile does exist, the key
//           in the KeyFile is installed into the CSP and used.//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.//       When specifying the KeyFile, the location of the KeyFile should be//       relative to the project output directory which is//       %Project Directory%\obj\<configuration>. For example, if your KeyFile is//       located in the project directory, you would specify the AssemblyKeyFile
//       attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework//       documentation for more information on this.[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

4. Chamando o AutoCAD

Vamos então nas propriedades do projeto em Debug. Em Start Action ao invés da opção Start project selecionaremos o acad.exe na pasta onde está localizado. Dessa forma o AutoCAD será aberto ao invés da DLL (o que já não iria acontecer).

Em Start Options faremos as configurações para abrir o nosso desenho de teste. Para isso, como o AutoCAD será aberto partindo da pasta bin\debug vamos colocar lá em ....\Documents\Visual Studio 2013\Projects\AutoCAD 2014 Class Library\AutoCAD 2014 Class Library\bin\Debug o arquivo de exemplo que chamaremos de "CAD_TEST_DRAWING.dwg".

Então vamos escrever em Start Options: ""CAD_TEST_DRAWING" /product ACAD /language "en-US" /nologo"

O restante do texto é para deixar o AutoCAD abrir sem o logo de inicialização o que o deixa mais rápido para abrir.

4. Testando

Bom agora o Projeto de Template está pronto vamos fazer um teste depurando o código. O AutoCAD deve abrir e poderemos carregar a DLL e executar o comando_teste.






Agora já estamos prontos para voltar ao Visual Studio e transformar esse projeto em um Template.

5. Transformando em um Template

Existe um Wizard do Visual Studio para criação de Templates a partir de projetos já prontos no Menu File > Export Template ...

O menu é bem empírico e no final você consegue ter um projeto *.zip modelo para novos projetos.





E finalmente nos podemos abrir um novo projeto e lá estará o nosso template.



Faça DOWNLOAD aqui do template que fiz nesse post.