winder / Universal-G-Code-Sender

A cross-platform G-Code sender for GRBL, Smoothieware, TinyG and G2core.
http://winder.github.io/ugs_website/
GNU General Public License v3.0
1.88k stars 762 forks source link

Show all Enabled Axes in DRO from XYZABC not just Linear Axes #1660

Open AndyCXL opened 3 years ago

AndyCXL commented 3 years ago

Is your feature request related to a problem? Please describe.imageShow all Enabled Axes in DRO from XYZABC not just Linear Axes

Describe the solution you'd like Right-click in DRO only shows XYZ and whether disabled. If XYZABC were all able to be enabled or disabled this way, then any 'has capability' axis from the actual machine could be displayed in DRO. If an axis is rotational, it would be nice if some visual distinction could be made versus a linear axis.

Describe alternatives you've considered I have hacked axisutils to declare A axis as Linear and thus show in DRO. This demonstrates that the underlying code and data reporting are there and able to work, so a 'formal' solution is likely not that difficult.

Additional context

breiler commented 3 years ago

You are using Grbl-Mega-5x right? It is not fully implemented in UGS to support multiple axises yet.

It should preferably have its own controller implementation that would detect how many axises that the hardware is configured with and then add that capability. It would then be shown automatically in the DRO: https://github.com/winder/Universal-G-Code-Sender/blob/master/ugs-platform/ugs-platform-plugin-dro/src/main/java/com/willwinder/ugs/nbp/dro/panels/DROPopup.java#L42

Here is an example on how its done with Grbl-ESP32: https://github.com/winder/Universal-G-Code-Sender/blob/master/ugs-core/src/com/willwinder/universalgcodesender/GrblEsp32Controller.java#L75

I think we have had suggestions on making it possible to change the visualization of the axises in the DRO where ABC should be shown in degrees by default. But I'm not sure how the visualization of the gcode should be done if it weren't rotational...

AndyCXL commented 3 years ago

Yes, Grbl Mega 5X on my Carbide 3D ShapeOKO 3XL with VFD spindle and a rotary axis upgrade. I have studied the Mega 5XL source in some detail and know the following:

AndyCXL commented 3 years ago

Comments towards the visualisation:

AndyCXL commented 3 years ago

I have a working instance of GrblMega5XController up and successfully connecting. During this I extract from the status strings the AxisOrder as reported from Mega5X and have instantiated a call (duly instantiated through the override layers) to obtain this string as it is needed in GrblUtils.java to correctly sequence and assign the MPos etc positions. For this last part to work, I need a lightweight way of getting the current controller instance, so I can call the new getAxisLetterOrder() method. GrblUtils has no current objects relating to the Controller - as I know I am deep in the bowels of the code here, I would appreciate a pointer as to how to achieve this...?

breiler commented 3 years ago

Thanks for the clarification about the status reports!

I think that you can create your own GrblMega5XUtils.java that is specialized in handling the status messages from GrblMega5X as it has deviated from Grbl. The util methods for parsing the status report would need the state of the controller configuration so I figure that those could be sent along as well.

So for instance, the previous method for parsing the status message in Grbl is done using GrblUtils.getStatusFromStatusStringV1:

/**
 * Parses a GRBL status string in in the v1.x format:
 * 1.x: <status|WPos:1,2,3|Bf:0,0|WCO:0,0,0>
 * @param lastStatus required for the 1.x version which requires WCO coords
 *                   and override status from previous status updates.
 * @param status the raw status string
 * @param reportingUnits units
 * @return the parsed controller status
 */
 public static ControllerStatus getStatusFromStatusStringV1(ControllerStatus lastStatus, String status, Units reportingUnits)

The new method for GrblMega5XUtils could be something like:

/**
 * Parses a GrblMega5X status string: <status|WPos:1,2,3,N|Bf:0,0|WCO:0,0,0,N>
 * 
 * @param lastStatus required for WCO coords
 *                   and override status from previous status updates.
 * @param status the raw status string
 * @param reportingUnits units
 * @param axisOrder a list of axises that will be reported by the controller. Normally [X, Y, Z] but could also be [X, Y, Z, Y] when the Y-axis is mirrored.
 * @return the parsed controller status
 */
 public static ControllerStatus getStatusFromStatusStringV1(ControllerStatus lastStatus, String status, Units reportingUnits, List<Axis> axisOrder)
AndyCXL commented 3 years ago

Your explanation allowed me to understand the structure of the code and classes better, and I can now see what is needed. First pass at this is written and builds. Now to test.

AndyCXL commented 3 years ago

I have explored this idea to the limit of my (limited) architectural knowledge of Java and UGS, and don't believe the weave within the UGS structure is within my grasp to code to incorporate a GrblMega5X method. That said, I have worked out the various individual pieces of code in order to do this... which someone can hopefully take and either frame-out the architectural needs so I can finish it, or take onboard themselves to do this.

In the meantime I have a dirty hack that makes what I need work, it's just not suitable for incorporation as-is and cannot deal with any arbitrary choice of axis sequencing coming from the GrblMega5X controller board.

New version of function within GrblUtils.java (or a new GrblMega5XUtils) that will be required: Adds axisOrder to the call, and through the call tree to be able to correctly interpret the axis report data

static private Position getPositionFromStatusString(final String status, final Pattern pattern, Units reportingUnits, String axisOrder) { / GrblMega5XController knows the reported Axis order, eg: XYZ or XYA or XYZYA // and added capabilities according to each unique axis letter, even if the count // of axes was greater (XYZYA is 4 distinct letters 5 positions). Position reporting // is per physical axis, so XYZYA is the string order even though Y is duplicated / Matcher matcher = pattern.matcher(status);

    // Do we have any matches, if so assign them to axes
    if (matcher.find()) {
        // Is Axis ordering default, or dictated by the controller
        // String axisOrder defines the order
        if ("".equals(axisOrder)) {
            axisOrder = "XYZABC";
        }

        // pX contains the integer position of X in axisOrder
        // indexOf returns 0th index of first match, or -1 if no match
        int pX = axisOrder.indexOf("X")+1;
        int pY = axisOrder.indexOf("Y")+1;
        int pZ = axisOrder.indexOf("Z")+1;
        int pA = axisOrder.indexOf("A")+1;
        int pB = axisOrder.indexOf("B")+1;
        int pC = axisOrder.indexOf("C")+1;

        // Position is a fixed order structure, indexOf bridges to axisOrder
        Position result = new Position(
                pX > 0 ? Double.parseDouble(matcher.group(pX)) : 0.00D,
                pY > 0 ? Double.parseDouble(matcher.group(pY)) : 0.00D,
                pZ > 0 ? Double.parseDouble(matcher.group(pZ)) : 0.00D,
                reportingUnits
        );

        result.a = pA > 0 ? Double.parseDouble(matcher.group(pA)) : 0.00D;
        result.b = pB > 0 ? Double.parseDouble(matcher.group(pB)) : 0.00D;
        result.c = pC > 0 ? Double.parseDouble(matcher.group(pC)) : 0.00D;

        return result;
    }
    // No match, default return null
    return null;
}

New version of functions in GrblController.java (or a new GrblMega5XController.java) that will be required: this extracts the GrblMega5X controller's reported axis order and makes it available to other modules in addition to updating the capabilities

static String axisOrder = "XYZABC"; static Pattern axisCountPattern = Pattern.compile("\[AXS:(\d):([XYZABC])]");

Optional getAxisCount(String response) { Matcher m = axisCountPattern.matcher(response); if (!m.find()) { return Optional.empty(); } return Optional.of(Integer.parseInt(m.group(1))); }

Optional getAxisOrder(String response) { Matcher m = axisCountPattern.matcher(response); if (!m.find()) { return Optional.empty(); } return Optional.of(m.group(2).trim()); }

// Parse axis details from status string - AndyCXL private void parseAxesFromStatus(final String response) { / [VER:1.1t.20210510:] [AXS:5:XYZYA] [OPT:VNMGPH,25,255,48] / Optional axes = getAxisCount(response); Optional order = getAxisOrder(response);

    if (axes.isPresent()) {
        this.capabilities.removeCapability(X_AXIS);
        this.capabilities.removeCapability(Y_AXIS);
        this.capabilities.removeCapability(Z_AXIS);
        this.capabilities.removeCapability(A_AXIS);
        this.capabilities.removeCapability(B_AXIS);
        this.capabilities.removeCapability(C_AXIS);

        /* Axes defines the number of distinct axes, Order defines the
        // sequence and any duplication, eg: dual-Y or dual-X configs
        // eg: 3=XYZ 3 distinct, 3=XYA 3 distinct, or 5=XYZYA 4 distinct
        // Ignore for now that Mega 5X allows letters other than ABC
        //
        // Strategy for coping with non XYZABC order, and duplicates:
        // Iterate 1..axes.get()
        //   Obtain nth axis letter from order
        //   Test if this.capabilities.hasCapability(n_AXIS) already exists
        //   and addCapability() if not
        //
        // Capabilities now added for each distinct axis letter, potentially
        // different (fewer) than the number of physical axes Mega5X reports
        */
        char nthChar;
        for (int n=0; n < axes.get(); n++) {
            nthChar = order.get().charAt(n);
            switch(nthChar) {
                case 'X':
                    if (!this.capabilities.hasCapability(X_AXIS))
                        this.capabilities.addCapability(X_AXIS);
                    break;
                case 'Y':
                    if (!this.capabilities.hasCapability(Y_AXIS))
                        this.capabilities.addCapability(Y_AXIS);
                    break;
                case 'Z':
                    if (!this.capabilities.hasCapability(Z_AXIS))
                        this.capabilities.addCapability(Z_AXIS);
                    break;
                case 'A':
                    if (!this.capabilities.hasCapability(A_AXIS))
                        this.capabilities.addCapability(A_AXIS);
                    break;
                case 'B':
                    if (!this.capabilities.hasCapability(B_AXIS))
                        this.capabilities.addCapability(B_AXIS);
                    break;
                case 'C':
                    if (!this.capabilities.hasCapability(C_AXIS))
                        this.capabilities.addCapability(C_AXIS);
                    break;
            }
        }
        // Record Axis ordering as a capability
        if (order.isPresent()) {
            axisOrder = order.get();
            this.capabilities.addCapability(AXIS_ORDERING);
        } else {
            axisOrder = "XYZABC";
        }
        // Log outcome, and to aid machine and setup diagnostics
        logger.log(Level.CONFIG, "Axes:{0} Order:{1}",
            new Object[] {axes.get(), order.get()}
        );
    }
}

Additions to CapabilitiesConstants.java that will be required:

public static final String A_AXIS = "A_AXIS"; public static final String B_AXIS = "B_AXIS"; public static final String C_AXIS = "C_AXIS"; public static final String AXIS_ORDERING = "AXIS_ORDERING";