Author Topic: .NET Project Structure  (Read 6322 times)

0 Members and 1 Guest are viewing this topic.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
.NET Project Structure
« on: January 29, 2015, 04:36:15 PM »
I'm back in the CAD world! I’m going to be creating a toolset using C#, possibly some LISP, and would love to get some feedback regarding structure of the toolset. I’ve been programming Android apps in Java recently, and the mindset for CAD is quite different (I think). Language wise C# seems pretty close to Java.  Initially I’ll be targeting AutoCAD, possibly port a version over to Briscad later

The toolset is initially intended for civil/landscape design. Users will be able to place blocks (outlets  and valves) and draw pipes (LWpolylines most likely). The toolset will run hydraulic calculations, quantities, calculate locations, quantities, standard design stuff.

So far I’ve looked at a best practices class by Scott McFarlane. Some great stuff in there about delegates that bent my brain some.

For an overall structure I’m looking at something like this:
  • Most of the work will be handled by a .NET dll
  • Overall Product information (pipe sizes, coefficients , model #s etc) kept in csv files in the installation folder
  • Individual object and design information stored in xData on the various blocks and polylines (flow, velocity, pressure, etc)
  • Standard blocks (maybe layers?) kept in a template dwg in the installation folder
  • Either a custom properties palette, or adding custom properties to the CAD properties palette
  • User settings kept in registry keys

As I’m getting into this, I’m realizing the bulk of my code may end up in my commands class, which doesn’t seem quite right.  Is this pretty standard, or is there  a better way of doing it? Do you typically have one class containing all commands, or do you break commands down into different classes for different types of commands?

Do you look at any of the bullet points and think ‘Oh, this guy has got it all wrong, he should do X’?

I’d love to get any feed back from those of you who've done this multiple times.

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: .NET Project Structure
« Reply #1 on: January 29, 2015, 07:08:14 PM »
I'd suggest a format other than CSV for data storage.  Its a little clunky and prone to problems like misplaced columns and not handling commas in text.  Difficult to edit directly, using Excel makes it prone to column removal and invalid data entry.  Also causes more data files to be created as mixing "tables" with different numbers of columns in the same CSV is difficult to manage.

XML provides a number of advantages, like not requiring all data in all parts (think jagged tables); complex search criteria using XPath; format and content validation through XSD schema; and standardized conversions (such as converting data to HTML reports) using XSL stylesheets.

Large amounts of data would probably be best handled through a database of some kind.  SQL syntax is useful for searching and using the System.Data namespace along with databinding provides some handy means of both pulling data and pushing changes without much work on the developers part.

I'd suggest using Extension Dictionaries and XRecords instead of XDATA.  Cleaner, provides named object access, and supports nesting (e.g. Dictionary hosting multiple dictionaries hosting multiple dictionaries hosting both Dictionaries and XRecords).

While there is nothing wrong with most of your code ending up in the command functions, it *does* suggest a linear programming methodology.  Maybe start thinking about objects ie. this block represents a valve - maybe I should create a Valve object with appropriate properties and methods.  I have pipe as well, and it has things in common with the valve, maybe I should have a base Component class to handle those common properties and methods instead.
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

nekitip

  • Guest
Re: .NET Project Structure
« Reply #2 on: January 30, 2015, 02:54:55 AM »
I'm sort of advanced beginner still. If it can help you at all, here is my view:
  • settings in XML, since I'm sure that you do not have a 50 mb settings file to need sql solution.
  • xdata for name of your app or your internal id in entity, xrecords for other
  • custom pallete if you have more than a few things to show
command class is just to call yor function that exists in some module or shared, static class
most likely, overrules will be used, pointing to your functions in those classes
You'll end up creating your own class "pipe", "valve", with your own properties and you'll need some list to hold this in place. If you do, here are some mistakes I have made:
-I've used obsevable collection (useful for CAD) since I could override a lot, but eventully, there was no room to override more
-then I've decided to create from zero, and created something that could be described as observablesorteddictionaryofcadobjects with a whole bunch of methods for search, replace... but eventually it became hard to debug and to add new feature (since it was large, I've found that I'm loosing time just following different program paths travel)
And then you realise that this is just one minor part of your solution and you are loosing a lot of time for features people don't even care. They will first tell you about textbox being too small, or your flow is not covering turbulent case, and not that the objects in your list are updating 1.5 times slower than you hoped for. So - do not waste too mouch time on CAD part, and make it simple to be faster to debug later and than a year later, when you return to your code - to be able to remember what is what.

other than that, some universal guidance is:
-decoupling code, reusable code, comments everywhere, function descriptions, function naming convention, function return policy if nothing (null)... and so on...

I'm sure someone will have more advices.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: .NET Project Structure
« Reply #3 on: January 30, 2015, 11:44:42 AM »
Great stuff so far. Thanks so much for the feedback. I feel like I've got a better handle on how to put this together already.

Regarding CommandMethods, I was hoping to pull everything out to factory/utility objects, which would make the command module much easier to read. Something like this:

Command class
Code: [Select]

       [CommandMethod("DRAWPIPE")]
        // let user pick points for pipe nodes
        public void DrawPipe()
        {
            PipeFactory pFactory = new PipeFactory();
            pFactory.pickNewPipes();
        }
PipeFactory class:
Code: [Select]
    class PipeFactory
    {
        /*allow user to pick points and draw polylines along those points with attached xData
         return: number of pipes added*/
        public int pickNewPipes()
        {
            return 0;
        }
       
        /* prompt user to select pipes to show mleader labels on
         return number of modified pipes*/
        public int labelAllPipes()
        {
            ObservableCollection<Pipe> allPipes = getAllPipes();
            foreach (Pipe pipe in allPipes)
            {
                pipe.setLabeled(true);
            }
            return allPipes.Count;
        }

        // in the active drawing, select all polylines w/ our xdata (filter)
        private ObservableCollection<Pipe> getAllPipes()
        {
            throw new NotImplementedException();
        }
    }

Pipe class:
Code: [Select]
    public class Pipe
    {
        public double InnerDiameter { get; set; }
        public double OuterDiameter { get; set; }
        public double Roughness { get; set; }

        public void setLabeled(bool isLabeled)
        {
            if (isLabeled)
            {
                /*draw new multileader
                 autocad transaction goes here*/
            }
            else
            {
                // find mleader label and remove it transaction goes here
            }
        }
    }

Where all the work with the user interface is done in the PipeFactory class. The tutorials I've looked at don't do it this way, instead, showing at least try/catch statements in the command methods, which I found bulky to read, and left me imagining a Commmand class thousands of lines long.  :no:

Collections! This is one of the changes for me coming from writing apps where I can initialize all my data and object collections on app startup, and have them in memory. It  seems that I need to initialize my datasets at the beginning of every command, since I don't have any objects that persist between commands (Or can I? If so, where do I initialize them?) Are ObservableCollections the way to go? Perhaps Dictionaries would be a good way to handle collections.

XData/ExtensionDictionaries: I got lost in the world of ExtensionDictionaries about a decade ago (holy crap, I guess I'm getting old) and ended up with a warped brain and reverting to putting xdata on all my objects, so I could easily filter for them via filtered selectionset. dgorsman: If I'm understanding you, instead of filtering selections by xData, you would just grab all the named objects in a allPipesDictionary to get a hold of the polylines in question?

Transactions: I read something here about reusing transactions and not nesting them. If that's the case, should I be moving transactions outside of single objects (one transcation per mleader added) and moving them to factory/utility methods (labelAllPipes gets one transaction committing multiple mleaders at once). Maybe there's a better way to abstract my objects to optimize transactions (holy buzzwords batman)



dgorsman

  • Water Moccasin
  • Posts: 2437
Re: .NET Project Structure
« Reply #4 on: January 30, 2015, 01:31:22 PM »
Don't confuse AutoCAD Extension Dictionary objects with the Dictionary<TKey,TValue>.  Functionally similar but completely different.

One of the first things to do is think about what data is going to be stored, where, and how its going to get in, out, and be modified.  Do you *really* need a roughness value for every single Pipe object in the file?  Or is it better to assign the Pipe a material, which can then be applied against a single look-up table?  Where is the object data going to be stored?  On a 3D solid?  Centerline for that solid?  Proxy block reference?  Handle/filename dual key for an external storage mechanism?

You might have a data structure associated with a center line of a pipe, looking like this:

My Application Dictionary (keeps your stuff separate)
   Object settings Dictionary
      Locked XRecord: (bool value)
      Layer XRecord: string or reference to layer object or whatever
   Object properties Dictionary
      Diameter XRecord: inner and outer diameter (doubles)

That way the pipe stores its individual information and can be recalled after the drawing is closed.

And so on.  While you can natively filter by XDATA name (not content) in a selection set, its not much more work to create a new selection tool which filters by your criteria and returns them as appropriate, such as a List<Component>.  You could keep a collection (custom or otherwise) and simply work from that, provided the various objects have a means of getting to the drawing objects if needed (handle, object ID, whatever floats your doubles).
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

nekitip

  • Guest
Re: .NET Project Structure
« Reply #5 on: January 31, 2015, 02:54:14 PM »
It  seems that I need to initialize my datasets at the beginning of every command, since I don't have any objects that persist between commands (Or can I? If so, where do I initialize them?) Are ObservableCollections the way to go? Perhaps Dictionaries would be a good way to handle collections.
You will learn that you have to separate your code anyway, command class is just to call your functions (in your class, defined as shared/static). It sort of "translates" or "points" autocad commands to your class, and there are real functions. But your data is not there, it is in a separate collection, initialised at that "friend module" or init sub of application (check init and terminate subs in begginer examples).
Remeber also that xrecords can hold just about anything (IRC gig or two), so serialize your data into it, and do it at the same time you have now data into. This will kept is safe if power goes down, since autocad autosave option will work for you, and also, this solves undo/redo; and keep xdata filled only with app name, or id or something if you must.
You will have your class (for example in VB Friend Module), or some other type of class that will hold your data and is started at the init of your program (check first two subs in every example you can find, and you will see initalize and terminate subs)
There are also various ways to keep your data, play and see what fits you. (remember, app is once initalized ever, but I'm not sure so for command class, it may be per document)
As for ObservableCollection - i told you my mistakes. If you want to override parts of it, its pretty fast for putting, removing things and informing UI (especially if you work in WPF which is dead or not debateble). However, Dictionaries have their value in being quick to find item by key. Now, also to share some thoughts - as I've said, as an advice - you can build your collection/dictionary class whatever way you see fit, but dont play too much, because, the logical way to store your data in dict is by (key=ObjectID, <your data class>). And this works, but in the real world - how many times you need to access ONLY by key (objectid)? You will maybe need to sort your data by flow. Dictionary is not helping you here. Depends. So, if you follow this advice of mine - do not overinvest yourself in creating perfect data holder, since you are going to lose time, and yet only the real world will show you real problems. Then you will know.
But if you must choose something, as a start, choose dict. If you're part of a team, then it's a different story, since they will play with program logic, you play with CAD.

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: .NET Project Structure
« Reply #6 on: February 02, 2015, 08:14:54 AM »
Just wanted to throw out there that the only reason to use an ObservableCollection over any other IList or ICollection is because you need to know when the collection has changed.  Great feature when you're bound to a UI, not so much when you're just storing Acad objects for manipulation.  There is a lot of extra overhead in an ObservableCollection compared to IList. Now if you need events fired every time an object is added or removed from the collection then ObservableCollection is your class.
Revit 2019, AMEP 2019 64bit Win 10

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: .NET Project Structure
« Reply #7 on: February 03, 2015, 10:12:56 PM »
You could also consider https://msdn.microsoft.com/en-us/library/dd867973.aspx if you want to have more granular control over notification events, or parhaps ... https://peteohanlon.wordpress.com/2008/10/22/bulk-loading-in-observablecollection/
« Last Edit: February 03, 2015, 10:16:07 PM by CADbloke »

nekitip

  • Guest
Re: .NET Project Structure
« Reply #8 on: February 04, 2015, 04:32:36 AM »
Just wanted to throw out there that the only reason to use an ObservableCollection over any other IList or ICollection is because you need to know when the collection has changed.  Great feature when you're bound to a UI, not so much when you're just storing Acad objects for manipulation.  There is a lot of extra overhead in an ObservableCollection compared to IList. Now if you need events fired every time an object is added or removed from the collection then ObservableCollection is your class.
The problem of AutoCAD is that it kind of likes bulk operations. The other problem is that sometime you need to hold your objects (be them cad objects or .net) and forget about them until something happens.
Observablecollection is great in that sense since it can be overriden a lot. Performance, although slower, is still pretty good, and you will lose cpu time on other places in AutoCAD.
Here is one quick example i've found now (but didn't test), similiar to what I've done, and a good starter to intrigue you. http://www.codeproject.com/Tips/694370/How-to-Listen-to-Property-Chang
So, as you see, you'll now have a chance to see for each object when individual property changed, like download is finished, process is done, object has been modified... (just remember that your .NET class object that has changed must implement inotifychanged interface), remember that change has happen and then wait for some event (like application idle, command ended or whatever), and perform bulk operation. Again, this all depends on what you need, but observablecollection is great because it allows this to easily be done.

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: .NET Project Structure
« Reply #9 on: February 04, 2015, 07:36:57 AM »
Just wanted to throw out there that the only reason to use an ObservableCollection over any other IList or ICollection is because you need to know when the collection has changed.  Great feature when you're bound to a UI, not so much when you're just storing Acad objects for manipulation.  There is a lot of extra overhead in an ObservableCollection compared to IList. Now if you need events fired every time an object is added or removed from the collection then ObservableCollection is your class.
The problem of AutoCAD is that it kind of likes bulk operations. The other problem is that sometime you need to hold your objects (be them cad objects or .net) and forget about them until something happens.
Observablecollection is great in that sense since it can be overriden a lot. Performance, although slower, is still pretty good, and you will lose cpu time on other places in AutoCAD.
Here is one quick example i've found now (but didn't test), similiar to what I've done, and a good starter to intrigue you. http://www.codeproject.com/Tips/694370/How-to-Listen-to-Property-Chang
So, as you see, you'll now have a chance to see for each object when individual property changed, like download is finished, process is done, object has been modified... (just remember that your .NET class object that has changed must implement inotifychanged interface), remember that change has happen and then wait for some event (like application idle, command ended or whatever), and perform bulk operation. Again, this all depends on what you need, but observablecollection is great because it allows this to easily be done.

Being notified of property changes is all from INotifyPropertyChanged and has nothing to do with ObservableCollection<T>.  If you just need to know when a property changes then you should implement INotifyPropertyChanged on your class. ObservableCollection<T>, all it does is notify when an object has been added or an object has been removed from the collection.  It doesn't implement bulk operations out of the box, i.e. you can't AddRange or RemoveRange like an IList<T>. I'm sure you have valid reasons for using ObservableCollection<T>. I'm just saying that your suggestion has led Atook to post sample code using ObservableCollection<T> when List<T> would have worked better.
Revit 2019, AMEP 2019 64bit Win 10

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: .NET Project Structure
« Reply #10 on: February 04, 2015, 03:17:56 PM »
Looks like I should work with basic lists until I find a need to have an observable list. I can complicate things from there.

Regarding data storage: this is one of my original points of confusion/question. Originally I was going to write properties such as flow/velocity to xdata, and have some type of pipe constructor that takes a polyline as an argument and read calculated values (flow, pressure) from the xdata. Other properties (roughness, diameter) could be read from a table using a key that was also written in xData to the polyline. Length and elevations can be calculated from the polyline object.

dgorsman: Your example looks like an extension dictionary with sub dictionaries and xrecords. I imagine this route gives me more flexibility with what can be stored. Going this route, I'd just store an application ID in xData (for selection purposes) then parse my dictionary to retrieve the properties needed. Is the advantage of this speed/flexibility?

I keep seeing mention of accessing objects after the drawing is closed. In my naive view, I can't imagine why I'd want this. I can only imagine the user wanting this information that they have the drawing open for. Is there a common use for this that I'm missing? Maybe pulling objects out of a template drawing?

Thanks for the discussion so far. I think for now I'll finish up these tutorials, and post more direct questions as I muddle my way through the first draft.

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: .NET Project Structure
« Reply #11 on: February 04, 2015, 03:50:56 PM »
Size and flexibility.  XDATA relies on a very specific data order.  You *must* ensure you have the same order and number of elements that is expected.  To get information in/out you have to use an integer index to reference the data which can be a PITA to manage even if you set up enums or constants.  One of the things I work hard on is being insensitive to the order of the data unless that order makes sense.  Consider something as simple as coordinates - you could code everything as 0 for X, [1] for Y, [2] for Z.  But wouldn't it be easier to work with if the X is available as "X", Y as "Y", and Z as "Z" (have a look at the Point3D struct).
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

Jeff H

  • Needs a day job
  • Posts: 6150
Re: .NET Project Structure
« Reply #12 on: February 04, 2015, 04:11:56 PM »
The only reason I have ran across that I rather use XDATA is XDATA contained in AttributeDefinitions will be cloned to the AttributeReferences built from it for each BlockReference.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: .NET Project Structure
« Reply #13 on: February 04, 2015, 04:15:00 PM »
Size and flexibility...

Sounds like good reasons to me. Thanks :)

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: .NET Project Structure
« Reply #14 on: February 04, 2015, 04:48:05 PM »
The thing to watch with XDATA is ATTSYNC - it resets / deletes any XDATA you have on Attributes. See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-512c.htm,topicNumber=d0e199454 & http://stackoverflow.com/a/3927807/492. You should ttest that and see if it does what you expect.

Here is one quick example i've found now (but didn't test), similiar to what I've done, and a good starter to intrigue you. http://www.codeproject.com/Tips/694370/How-to-Listen-to-Property-Chang
Read the comments on that post - that solution needs further investigation and testing.
« Last Edit: February 04, 2015, 04:53:03 PM by CADbloke »