Author Topic: Writing C# plugins on linux that creates a new MOP  (Read 22361 times)

Offline Knas

  • Ewok
  • *
  • Posts: 18
    • View Profile
Writing C# plugins on linux that creates a new MOP
« on: February 04, 2022, 03:38:06 am »
Hello, i need to write a quick and dirty plugin for CamBam under Linux and it seems C# is the best choice for it, i'm currently using MonoDevelop but i'm not beholden to it.

After 2 days of frustration with serialization not working, typecasting issues, generally not being a C# guy (i've done programming since -89 tho in various languages) i figure i should just ask for help.

Basically i just need a working example to compile with all the bells and whistles so i can make a simple MOP that - as far as i understand it - besides doing various XYZ movements (based on pointlists) has to go in and either through post processing or custom headers do a movement of the "V" axis. This is for a selective soldering robot hence the weird axis reference. Any examples / hints very welcome, or i can supply the code i've written so far and you can tell me what i've done wrong.

Many thanks

Karl

Offline ThisAmplifierIsLoud

  • Storm Trooper
  • ***
  • Posts: 231
  • Jam it !
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #1 on: February 04, 2022, 08:26:48 am »
Welcome to the club !

Believe, I know ...  you can send a lot of time for that. ;D
maybe you can re-use something from here :

The code I posted is not actual, but it should show how create mops using c#

https://cambamcnc.com/forum/index.php?topic=9560.30

Best regards

Bernhard
best regards

Bernhard

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5333
  • Made in England
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #2 on: February 04, 2022, 15:52:48 pm »
Attach whatever code you have here.

You can also look at these plugins.

VEngrave plugin
Trochoidal plugin; https://github.com/jkmnt/matmill
ThreadMill plugin
Slotter plugin
Laser Dotter plugin
Lathe plugin

« Last Edit: February 04, 2022, 15:57:50 pm by EddyCurrent »
Filmed in Supermarionation

Offline Knas

  • Ewok
  • *
  • Posts: 18
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #3 on: February 04, 2022, 15:53:28 pm »
Thanks Bernhard

There's obviously something im missing in general about this whole thing, i've succesfully made my own MOP (even though CamBam says its not serialized so no data can be saved apparently) and i've resorted to doing what needs to be done the dirtiest way possible; just editing the CustomMOPHeader. Unfortunately, it seems despite whatever my attempts, the value is NULL both according to the debugger and CamBam. Here's a little snippet of all of the (possibly stupid) things i'm doing in my desperate attempts to edit it:

Code: [Select]
                    MOPSelectiveSoldering SelectiveSoldering = new MOPSelectiveSoldering(view.CADFile, view.SelectedEntities);
                    SelectiveSoldering.CustomMOPHeader.ClearCache();
                    SelectiveSoldering.CustomMOPHeader.SetState(CBValueStates.Value);
                    SelectiveSoldering.CustomMOPHeader.NewValue("G53 G0 X1");
                    SelectiveSoldering.CustomMOPHeader.Parse("G53 G0 X1");
                    SelectiveSoldering.CustomMOPHeader.SetValue("G53 G0 x1");
                    SelectiveSoldering.CustomMOPHeader.SetState(CBValueStates.Value);
                    SelectiveSoldering.CustomMOPHeader.SetCache("G53 G0 X1");
                    view.CADFile.ActivePart.MachineOps.Add(SelectiveSoldering);

Welcome to the club !

Believe, I know ...  you can send a lot of time for that. ;D
maybe you can re-use something from here :

The code I posted is not actual, but it should show how create mops using c#

https://cambamcnc.com/forum/index.php?topic=9560.30

Best regards

Bernhard

Offline Knas

  • Ewok
  • *
  • Posts: 18
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #4 on: February 04, 2022, 15:58:15 pm »
Attach whatever code you have here.

Aight, here it goes. Btw i cannot for the life of me get CamBam XML data like [Description] to work with the compiler, i've tried including everything i can think of but no dice - hints appreciated. As can be seen by the code, i'm simply trying to modify the CustomMOPHeader at this point.

Code: [Select]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using System.Drawing;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;


using CamBam;
using CamBam.UI;
using CamBam.Geom;
using CamBam.Util;
using CamBam.CAD;
using CamBam.CAM;
using CamBam.Library;
using CamBam.Values;

namespace CBSelectiveSoldering
{
    public class SelectiveSolderingPlugin
    {
        protected static CamBamUI _ui;

        // This is the main entry point into the plugin.
        public static void InitPlugin(CamBamUI ui)
        {
            // Store a reference to the CamBamUI object passed to InitPlugin
            _ui = ui;

            // Create a new menu item in the top Plugins menu
            //ToolStripSeparator toolStripSeparator = new ToolStripSeparator();
            ToolStripItem ts = ui.Menus.mnuMachining.DropDownItems.Add("-");

            ToolStripItem mi = ui.Menus.mnuMachining.DropDownItems.Add("Selective soldering");
            mi.Click += new EventHandler(InsertMOP);

            if (CADFile.ExtraTypes == null)
            {
                CADFile.ExtraTypes = new List<Type>();
            }
            CADFile.ExtraTypes.Add(typeof(MOPSelectiveSoldering));
        }

        static void InsertMOP(object sender, EventArgs e)
        {
            ICADView view = _ui.ActiveView;



            //object[] vs =

            if (view.CADFile.ActivePart is null)
            {
                CAMPart part = new CAMPart("Solder run");
                view.CADFile.Parts.Add(part);
                view.CADFile.ActivePart = part;
            }

            foreach (object solderPoint in view.SelectedEntities)
            {
                if (solderPoint is PointList)
                {
                    MOPSelectiveSoldering SelectiveSoldering = new MOPSelectiveSoldering(view.CADFile, view.SelectedEntities);
                    SelectiveSoldering.CustomMOPHeader.ClearCache();
                    SelectiveSoldering.CustomMOPHeader.SetState(CBValueStates.Value);
                    SelectiveSoldering.CustomMOPHeader.NewValue("G53 G0 X1");
                    SelectiveSoldering.CustomMOPHeader.Parse("G53 G0 X1");
                    SelectiveSoldering.CustomMOPHeader.SetValue("G53 G0 x1");
                    SelectiveSoldering.CustomMOPHeader.SetState(CBValueStates.Value);
                    SelectiveSoldering.CustomMOPHeader.SetCache("G53 G0 X1");
                    view.CADFile.ActivePart.MachineOps.Add(SelectiveSoldering);
                }
            }
            //int i = vs.Length;
        }
    }
    /*}

    namespace CamBam.CAM
    {*/

    [HasAdvancedProperties]
    [XmlRoot("mop")]   
    [XmlInclude(typeof(MOPPocket))]
    [XmlInclude(typeof(MOPProfile))]
    [XmlInclude(typeof(MOPFromGeometry))]
    [XmlInclude(typeof(MOPNCFile))]
    [XmlInclude(typeof(MOPDrill))]
    [XmlInclude(typeof(MOPEngrave))]
    [XmlInclude(typeof(MOPBasRelief))]
    [XmlInclude(typeof(MOP3DSurface))]

    [XmlType("MOPSelectiveSoldering")]
    public class MOPSelectiveSoldering : CamBam.CAM.MOPFromGeometry
    {

        protected double _SafeHeight;
        protected double _RampInY;
        protected double _Heatupdwell;
        protected double _SolderFeed;
        protected double _SolderMove;

        protected object[] solderPoints;
        protected ShapeList solderShapes;

        /*        #region OperationProperties
                #region SafeHeight
                const double default_safeHeight = 15;
                [XmlAttribute("Safe height")]
                [DefaultValue(default_safeHeight)]
                public CBValue<double> SafeHeight
                {
                    get;
                    set;
                }
                #endregion
                    */

        [XmlAttribute("Ramp in Y")]
        [DefaultValue(-5)]
        public double RampInY
        {
            get { return _RampInY; }
            set { _RampInY = value; }
        }

        [XmlAttribute("Heatup dwell")]
        [DefaultValue(0.5)]
        public double HeatupDwell
        {
            get { return _Heatupdwell; }
            set { _Heatupdwell = value; }
        }

        [XmlAttribute("Solder feed")]
        [DefaultValue(250)]
        public double SolderFeed
        {
            get { return _SolderFeed; }
            set { _SolderFeed = value; }
        }

        [XmlAttribute("Solder move")]
        [DefaultValue(36)]
        public double SolderMove
        {
            get { return _SolderMove; }
            set { _SolderMove = value; }
        }

        public MOPSelectiveSoldering(CADFile cadFile, object[] _solderPoints) {
            //insertSolderPoints(_solderPoints);

            this.Name = "Selective soldering";
            _RampInY = -5;
            _Heatupdwell = 0.5;
            _SolderFeed = 250;
            _SolderMove = 36;
        }

        [XmlIgnore]
        public override string MOPTypeName
        {
            get { return "Selective Soldering"; }
        }
        /*
                public override CamBam.CAM.MachineOp Clone()
                {
                    return new MOPSelectiveSoldering(this);
                }
                */
        private void SetDirty()
        {
            if (CADFile != null)
            {
                CADFile.Modified = true;
            }
        }

        protected override void _GenerateToolpathsWorker()
        {
            //ThisApplication.MsgBox("_Generate");
            try
            {
                ClearToolpaths();
                //generateSolderPoints();


            }
            catch (Exception e)
            {
                ThisApplication.MsgBox("Exception");
            }
        }

        public override void GenerateToolpaths(CADFile CADFile)
        {
            //generateSolderPoints();
            ThisApplication.MsgBox("Generate");
        }

        public void insertSolderPoints(object[] _solderPoints)
        {
            solderPoints = new object[_solderPoints.Length];
            _solderPoints.CopyTo(solderPoints, 0);
            foreach (object solderPoint in solderPoints)
            {
                if (solderPoint is PointList)
                {
                   // solderShapes.AddEntity(this.CADFile, ((PointList)solderPoint).ID);
                }
            }
        }

        private void generateSolderPoints()
        {
            solderShapes = new ShapeList(solderPoints.Length );
            ToolpathSequence tseq = new ToolpathSequence(this);
            foreach (object solderPoint in solderPoints)
            {
                if (solderPoint is PointList)
                {
                    int depthIndex = 0;
                    int offsetIndex = 0;

                    solderShapes.AddEntity( ((Entity) solderPoint) );
                    //tseq.Add(0, offsetIndex: offsetIndex++, ((ShapeListItem) solderPoint).EntityID,  )
                }
            }


            SetToolpathSequence(tseq);
        }

    }
}

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5333
  • Made in England
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #5 on: February 04, 2022, 16:06:59 pm »
All the plugins I mentioned earlier look like they add a new type of MOP so that's what what you need to look at.
Filmed in Supermarionation

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5333
  • Made in England
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #6 on: February 04, 2022, 16:31:33 pm »
At the top of the MOP section I changed it to this,


Code: [Select]

        //[HasAdvancedProperties]
        //[XmlRoot("mop")]
        //[XmlInclude(typeof(MOPPocket))]
        //[XmlInclude(typeof(MOPProfile))]
        //[XmlInclude(typeof(MOPFromGeometry))]
        //[XmlInclude(typeof(MOPNCFile))]
        //[XmlInclude(typeof(MOPDrill))]
        //[XmlInclude(typeof(MOPEngrave))]
        //[XmlInclude(typeof(MOPBasRelief))]
        //[XmlInclude(typeof(MOP3DSurface))]

        //[XmlType("MOPSelectiveSoldering")]

        [Serializable]
        public class MOPSelectiveSoldering : CamBam.CAM.MOPFromGeometry
        {

       public MOPSelectiveSoldering()
        {
        }


Adding, [Serializable]

and adding this parameterless constructor that CamBam asked for in the error message.

public MOPSelectiveSoldering()
        {
        }


It will now save !

For all the other bells and whistles have a look at this excellent code by JK for the Trochoidal plugin; https://github.com/jkmnt/matmill/blob/master/mop.cs
« Last Edit: February 04, 2022, 16:41:15 pm by EddyCurrent »
Filmed in Supermarionation

Offline Knas

  • Ewok
  • *
  • Posts: 18
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #7 on: February 04, 2022, 17:05:34 pm »
Thank you for the answer, i must have removed it but i tried using [Serializable] a few times before, but despite my best efforts i get a "type or namespace cannot be found"-error, perhaps this is indicative of a larger problem with my mono installation? I Will definitely look into the trochoidal plugin, as it happens my fave tool path :D

Offline Dragonfly

  • CNC Jedi
  • *****
  • Posts: 2680
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #8 on: February 04, 2022, 17:21:15 pm »
@Knas
I am too far behind everybody in this thread with regard to programming.
Just want to share an observation, maybe appropriate, maybe not.
The 'Trochoidal pocket' which is a custom defined MOP cannot be copy/pasted under Linux (Mono). Attempt to paste produces an error message.
While under Windows there is no problem with copy/paste. Since you are developing with Mono maybe there are unresolved issues in the latter compared to MS .NET.

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5333
  • Made in England
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #9 on: February 04, 2022, 18:38:07 pm »
Knas,

If it helps for testing purposes, I've attached the compiled dll that will save and open again on my system.

Removed now the issue has been resolved.
« Last Edit: February 07, 2022, 11:20:18 am by EddyCurrent »
Filmed in Supermarionation

Offline Knas

  • Ewok
  • *
  • Posts: 18
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #10 on: February 04, 2022, 19:29:48 pm »
Knas,

If it helps for testing purposes, I've attached the compiled dll that will save and open again on my system.

Thank you so much, i did some googling and it turns out that [Serializable] with friends is somehow deprecated under later .NET version? or something? Tbh i didn't quite get it but solved the whole issue by adding a nuget package called 'Shim' per the suggestion of another Forum.

Progress! I can save and stuff now!

Unfortunately, i'm still at a loss on how to do the simplest editing of the CustomMOPHeader  :P

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5333
  • Made in England
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #11 on: February 04, 2022, 20:12:06 pm »
Try this;

Code: [Select]
foreach (object solderPoint in view.SelectedEntities)
                {
                    if (solderPoint is PointList)
                    {
                     MOPSelectiveSoldering SelectiveSoldering = new MOPSelectiveSoldering(view.CADFile, view.SelectedEntities);
                     SelectiveSoldering.CustomMOPHeader = new CamBam.Values.CBValue<string>("Hello World");
                    //SelectiveSoldering.CustomMOPHeader.ClearCache();
                    //SelectiveSoldering.CustomMOPHeader.SetState(CBValueStates.Value);
                    //SelectiveSoldering.CustomMOPHeader.NewValue("G53 G0 X1");
                    //SelectiveSoldering.CustomMOPHeader.Parse("G53 G0 X1");
                    //SelectiveSoldering.CustomMOPHeader.SetValue("G53 G0 x1");
                    //SelectiveSoldering.CustomMOPHeader.SetState(CBValueStates.Value);
                    //SelectiveSoldering.CustomMOPHeader.SetCache("G53 G0 X1");
                    view.CADFile.ActivePart.MachineOps.Add(SelectiveSoldering);
                    }
                }


Filmed in Supermarionation

Offline Knas

  • Ewok
  • *
  • Posts: 18
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #12 on: February 07, 2022, 02:06:26 am »
Thank you everyone for helping me, got a very quick and dirty plugin that works splendidly for my purposes now!

Figured i may as well post the code here for future references if anyone needs a extremely bare bones piece that will compile under Monodevelop / Linux. Main thing about it that was different for me was figuring out i had to add the Nuget package "Shim" in order to use the otherwise deprecated [Serialize] statement.

Code: [Select]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml.Serialization;

using CamBam;
using CamBam.UI;
using CamBam.Geom;
using CamBam.CAD;
using CamBam.CAM;

namespace CBSelectiveSoldering
{
    public class SelectiveSolderingPlugin
    {
        protected static CamBamUI _ui;

        // This is the main entry point into the plugin.
        public static void InitPlugin(CamBamUI ui)
        {
            // Store a reference to the CamBamUI object passed to InitPlugin
            _ui = ui;

            // Create a new menu item in the top Plugins menu
            //ToolStripSeparator toolStripSeparator = new ToolStripSeparator();
            ToolStripItem ts = ui.Menus.mnuMachining.DropDownItems.Add("-");

            ToolStripItem mi = ui.Menus.mnuMachining.DropDownItems.Add("Selective soldering");
            mi.Click += new EventHandler(InsertMOP);

            if (CADFile.ExtraTypes == null)
            {
                CADFile.ExtraTypes = new List<Type>();
            }
            CADFile.ExtraTypes.Add(typeof(MOPSelectiveSoldering));
        }

        static void InsertMOP(object sender, EventArgs e)
        {
            ICADView view = _ui.ActiveView;

            if (view.CADFile.ActivePart is null)
            {
                CAMPart part = new CAMPart("Selective Soldering");
                view.CADFile.Parts.Add(part);
                view.CADFile.ActivePart = part;
            }

            MOPSelectiveSoldering SelectiveSoldering = new MOPSelectiveSoldering(view.CADFile, view.SelectedEntities);
            view.CADFile.ActivePart.MachineOps.Add(SelectiveSoldering);

        }
    }

    [Serializable]
    public class MOPSelectiveSoldering : CamBam.CAM.MOPFromGeometry
    {

        protected double _SafeHeight;
        protected double _RampInY;
        protected double _Heatupdwell;
        protected double _SolderFeed;
        protected double _SolderMove;
        protected double _RampInFeed;
        protected double _PostFeedDwell;

        protected object[] solderPoints;
        protected ShapeList solderShapes;

        //[CBKeyValue, Category("Selective Soldering"), DefaultValue(-5), DisplayName("Ramp in Y")]
        [XmlAttribute("Ramp in Y")]
        [DefaultValue(-5)]
        public double RampInY
        {
            get { return _RampInY; }
            set { _RampInY = value; }
        }

        [XmlAttribute("Ramp in feed")]
        [DefaultValue(500)]
        public double RampInFeed
        {
            get { return _RampInFeed; }
            set { _RampInFeed = value; }
        }

        [XmlAttribute("Heatup dwell")]
        [DefaultValue(0.5)]
        public double HeatupDwell
        {
            get { return _Heatupdwell; }
            set { _Heatupdwell = value; }
        }

        [XmlAttribute("Solder feed")]
        [DefaultValue(150)]
        public double SolderFeed
        {
            get { return _SolderFeed; }
            set { _SolderFeed = value; }
        }

        [XmlAttribute("Solder feed")]
        [DefaultValue(36)]
        public double SolderMove
        {
            get { return _SolderMove; }
            set { _SolderMove = value; }
        }

        [XmlAttribute("Post feed dwell")]
        [DefaultValue(1.5)]
        public double PostFeedDwell
        {
            get { return _PostFeedDwell; }
            set { _PostFeedDwell = value; }
        }

        private void initValues()
        {
            this.Name = "Selective soldering";
            _RampInY = -5;
            _RampInFeed = 1750;
            _Heatupdwell = 0.5;
            _SolderFeed = 5000;
            _SolderMove = 300;
            _PostFeedDwell = 1.5;
        }

        public MOPSelectiveSoldering()
        {
            initValues();
        }

        public MOPSelectiveSoldering(CADFile file, object[] objects) : base(file, objects)
        {
            initValues();
        }

        [XmlIgnore]
        public override string MOPTypeName
        {
            get { return "Selective Soldering"; }
        }

        protected override void _GenerateToolpathsWorker()
        {
            try
            {
                ClearToolpaths();
                generateSolderPoints();
                MachineOpStatus = MachineOpStatus.OK;
            }
            catch (Exception e)
            {
                ThisApplication.MsgBox("Exception");
            }
            finally
            {
                _GenerateToolpathsFinal();
            }
        }

        private void generateSolderPoints()
        {
            string headerMod = new string('0', 0);
            headerMod = "G90 G0 Z" + this.ClearancePlane.Value.ToString() + "\r\n";

            for (int i = 0; i < this.PrimitiveIds.Length; i++) {
           
                if (this.CADFile.FindPrimitive(this.PrimitiveIds[i]) is PointList)
                {
                    Point3F[] points = ((PointList)this.CADFile.FindPrimitive(this.PrimitiveIds[i])).Points.ToArray();
                    for (int j=0; j< points.Length; j++)
                    {
                        headerMod = headerMod +
                            "G0 X" + points[j].X.ToString() + " Y" + (points[j].Y + (0 - this.RampInY)).ToString() + "\r\n" +
                            "F" + this.RampInFeed.ToString() + " G91 G1 Z" + (this.StockSurface.Value - this.ClearancePlane.Value) + " Y" + this.RampInY.ToString() + "\r\n" +
                            "G4 P" + this.HeatupDwell.ToString() + "\r\n" +
                            "F" + this.SolderFeed.ToString() + " G1 V" + this.SolderMove.ToString() + "\r\n" +
                            "G4 P" + this.PostFeedDwell.ToString() + "\r\n" +
                            "G90 G0 Z" + this.ClearancePlane.Value.ToString() + "\r\n"
                            ;
                    }
                }

            }
            this.CustomMOPHeader = new CamBam.Values.CBValue<string>(headerMod);
        }

    }
}

Offline EddyCurrent

  • CNC Jedi
  • *****
  • Posts: 5333
  • Made in England
    • View Profile
Re: Writing C# plugins on linux that creates a new MOP
« Reply #13 on: February 07, 2022, 07:41:10 am »
Nice one !
Filmed in Supermarionation