Update

So, I haven’t posted for a while. The main reason for this is because I’ve found a new job. I’m now coding insurance software in c#.

While many people program for microsoft dynamics in x++, I was still able to find bugs and things that weren’t well documented on the internet. Perfect fodder for posts. However, there are a ton more developers for c#, and I’m not finding as much stuff to talk about.

Also, this blog was started with the intention of making me more appealing to future job positions. Since I’m not currently looking for a job, that motivation is gone. In a few years, I might be back.

Rule Engine – Job Conditions

Finally we come to the business application for which I built the rule engine. In our plant we run plastic through press, lamination, slitter, and pouch machines. A work order might be for an item than runs through four machines; this work order would have four jobs. Job conditions tell the machine operator how to setup the machine for this item – examples include how hot to run the blades, what power to run the laser at, or what type of glue to use when laminating two sheets of plastic together.

The configuration looks like this:

ID: This field is used in formulas. For instance, the first job condition in the screenshot is LAM_Durometer. The second and third rows reference this run condition in their solution formula.

Display Order: Determines what order the job conditions are displayed to the user. Also a job condition can only affect other job conditions that have a higher display order, so as to prevent an infinite loop where two job conditions both effect each other.

English/Spanish: What the user sees.

Applicability: This is the first rule. It determines if the job condition applies to the current job. To date the formulas are using machine information, job information, or item information.

IsCalculated? If the solution is calculated, then the formula retrieves a value. If it’s remembered from previous jobs, then the formula produces a match string, and then we look through previous jobs and find the latest one with that job condition having the same match string. For instance, the coat weight in the screenshot varies depending on the material, so the match string is the raw material.

Solution: The second rule, how it is used depends on isCalculated.

At the top of the screen you have some buttons. The condition dropdown is useful if you want your job condition to be multiple choice (instead of just accepting any string). The Applicability Formula and Solution Formula bring up a screen that makes it easier to modify rules:

By selecting a input on the left side tree and either using the >> button or double clicking you can produce text on the right side. This exposes everything the rule engine can calculate, and also helps prevent typing mistakes. There are three general categories of inputs: current job data, previous job data, and general logic.

The validation area allows you to test the formula against any job in the system.

Of course, the screens above are for configuration. The operator won’t see them, their screen looks more like this:

The arrows at the top allow them to navigate to the next and previous jobs.

Rule Engine – Inheritance

AX 2012 doesn’t use inheritance a lot. Classes can inherit, but tables cannot, and I don’t think forms can (or rather, all forms inherit from ObjectRun). There is no inheritance for enums or Reports or anything except classes. But my rule engine is a class, so I have to decide whether to use inheritance or abstain.

The downside to inheritance is that you end up with two classes, and sometimes you’re not going to be able to know or remember which methods are in each class. You’ll have to switch between the classes, for instance if you’re in the child class and it references a method that’s in the parent class only and not overridden. The longer the inheritance tree, the more difficult it can be to determine which methods will be run.

But certainly in this situation, I feel the rewards outweigh the costs. It allows me to make a clear delamination in my project between functionality that’s basic to any rule engine that makes the rule engine work (ie, math, string functions), and logic that’s specific to my data structure. When we upgrade to 365 – a project that’s starting in a month or so – I’d expect the base rule engine functionality to remain the same, while the subclass will need to change to get Job data from the cloud.

Inheritance is specified with the ‘extends’ keyword.

Rule Engine – Trees

The ability to evaluate a formula requires a parse tree. First I setup a node class. Each node contains a left child, a right child, and a parent. Each node can contain a string – the constant or operation it’s expected to perform. Together, a collection of nodes is called a Tree. When each node can have up to two children, it’s called a binary tree. The leaves of the tree are the nodes at the bottom with no children, and in this case, they’re all data (numbers or strings). The nodes with children are all operators (<=, +, OR).

Evaluating a tree is simple. We start at the root node and the text tells us what to do with the children.

If the current node is the AND operation, then we return true if the left and right nodes both evaluate to true. The algorithm uses recursion to get the values of its children.

But how do we make the tree? My approach to this started with this post I found online:

https://www.geeksforgeeks.org/parse-tree-in-compiler-design/

Basically, as we read in each node we move around the tree in different directions depending on whether the node is data, an operation, or parenthesis. Everything was working fine until I came to the If statement. Originally, I’d wanted to use syntax like this:

if (<condition>) { <true return> } else {<false return>}

if (1>2) {1} else {2}

However, in order to navigate the node structure correctly I had to add two more keywords to trigger movement around the tree.

if (<condition>) then { <true return> } else {<false return>} end

if (1>2) then {1} else {2} end

I also had to break the binary tree paradigm. Every other node can have up to two children, but I added a third child just for the if operation:

So for an if statement, we evaluate the if_condition is correct and if so we return the left node, otherwise we return the right node.

Rule Engine – Test Driven Development

A rather large project I’m working on is to create a rule engine that can parse formulas that are written like code. Once this is complete I’ll be able to configure setup rules and quality check rules and Run condition rules – there will be a lot of possible uses.

However, next month we’re starting the 6 month project to upgrade Dynamics 2012 R1 to D365. There will be a code free, a code conversion, a data migration. Adding a lot of complexity now that hasn’t had time to be tested properly, then moving it to a new version seems like a recipe for disaster.

On the other hand, I’ve been reading a book on test driven development. I decided that this was a very good place to practice the principle. By building test cases, I can have better confidence that the rule engine works in 2012 R1, and after the upgrade I should still have the test cases and can easily identify areas that have been broken by the upgrade. Test Driven Development also makes it easier to refactor my code, which I’ve already had to do several times.

I’ve already covered the basics of unit testing in this post: https://wordpress.com/post/codeofcolors.wordpress.com/206

You can make a new test project from the projects menu:

A test project can have normal objects, but there’s also a test suite section where you put your testing classes:

Here are the test methods I’ve written so far:

This should also give you a better idea of what a rule engine does – it’s a code parser. Right now it passes all my tests above but it doesn’t pass some tests in the following method:

Still working on getting the if-else correct. I may have to change the syntax. Next week I’ll go into how the code of the rule engine works, and some of the other things I’m going to be adding that are specific to our business.

Run Conditions

So, in my facility we work with rolled plastic. For instance, we have finishing machines that take a roll of printed & laminated material as input and boxes of plastic bags as output. This machine will have a dozen bars spaced on it that heat, cut, compress, or otherwise interact with the material. Each time someone sets up the machine, they need to figure out where to place each of the bars and what temperature each of them should be… these things are different between one bag and the next and require time to get right. Time that the machine has to be running, churning through material we’ll never get to ship to the customer. What if we could tell the operator all these settings so they could get them right the first time?

We’re calling all these settings the run conditions. The run conditions that are temperature based will be consistent for bags that are constructed out of the same materials, even if the bag sizes are different. However, the run conditions that are positional based will be the same for any two bags that are the same dimensions, even if the bags are composed out of different materials. When I run a new bag A that’s the same material as old bag B and the same size as old bag C, the system should be able to tell it to use the temperature we used on B and the positioning we used on C.

Run Condition definition
What the operator sees – the run condition values for that bag

Different types of bag zipper also result in different heat levels, even for the same material. And as we move to more machines we’ll see more constraints as well. On the laminators and slitters, the speed of the machine impacts the tension we want the plastic under. Information on the artwork affects the laser placement on the slitter, as well as the press machines. When all this is done, I expect we’ll have dozens of different possible inputs that can be used to group items together that share the same run condition value. The goal is to never have to give the system a value if the system can come up with it itself.

As I was on my way to work this morning, it occurred to me that this is somewhat like linear regression in machine learning. Linear regressions has a list of variables and a weight to each of them. The run condition definition is just like the list of weights, all of them 0 or 1. The run condition values are like the variable values. If we were able to run our data through a linear regression model, we could come up with the values for materials and bag dimensions that have not yet been run.

A pipe dream… for now. AX 2012 is 10 years old and has no machine learning capabilities. We’re replacing it in a year with Dynamics 365 though, and I suspect that it does have machine learning, for the master planning at the very least.

AX Form Permissions

Over the past week, I’ve been working on a project for plate tracking that would create several checkboxes on a screen that many people visit. I needed the checkboxes to be visible for everyone, but I needed to be able to tightly control who had the boxes enabled.

So, I created priviledges and gave them access to my table. I even broke it down by field, for instance, the priviledge below can request a plate, but cannot approve or complete the plate.

However, try as I might, my users had access to the checkboxes even when they shouldn’t.

After trying everything I could think of, I was doing a security search on my table

and I found this:

Now, this was saying that the temBasePattern roles were accessing my table, but I’d never put it in there. After some more digging I found that these roles were accessing the form that I’d modified, and the form had my table as a datasource. Forms have permissions too!

I had to go into update, create, and delete and change the permissions for my table to be read. And then my users no longer had access, and my permissions objects were able to give them access as needed.

AX Multiple table group by

So, I came across this error yesterday:

Cannot mix unqualified ORDER BY or GROUP BY clauses with qualified ORDER BY or GROUP BY clauses.

The internet didn’t know what it meant. Here’s my code:

this error is happening because on the first group by I have <tablename.field> (wip.LayFlatWidth), whereas for the subsequent group bys I only have <field> (ibisNumAcross).

The solution is to put all the group by fields in a single statement, using <tablename.field> for each.

Join to same table

As part of a project I’m working on, I’m making a list of machine parts and the dates that they get replaced. I have a form that needs to show each part in the machine and the last time it was replaced.

So to do this, I’d need to find every record where there isn’t a newer version of that record in the table. This can be done with a not-exists join.

My first attempt failed (as did many subsequent). This is what I originally had:

But it gave me this Query:

As you can see in the second where clause, the right and left side are both comparing fields in the tem_machinePartReplacement table, and neither are looking at tem_machinePartReplacement_1.

So, after much searching through the internet, I discovered that you can change the links to be ranges. And then you can pass the table name into the range by using the datasource.name() method.

Much better. I couldn’t get the date to work no matter how I tried, but the recid should be sufficient to verify that I’ve got the latest record.

A CLR error occurred while invoking the scheduling Engine

For the entire time I’ve worked here, whenever I tried to schedule a work order in the Test environment, I got an error like this:

Scheduling the same work orders always works in Prod or Dev. So this is clearly an environment issue. The most common solution mentioned online is to go into your AX Server utility and uncheck a checkbox regarding hot-swapping of assemblies. That didn’t work for me.

Unfortunately, this was one place where stepping through the code had limitations. The error is occurring in code that is in a .dll, and cannot be viewed. I tried replacing the .dll but that didn’t help.

Then I found this useful post that explained how to get more information on the scheduling error:

I followed this and changed my code:

And I got this info message:

While interesting, I wasn’t sure what to make of this until I discovered that the same folder that has the .dll I was messing around with earlier also contains a .config file named “SchedulingEngine”. This is what that file looks like:

Guess where line 8 position 20 is? It’s the start of the word test, which is outside the formatted .xml and doesn’t make any sense. I removed that, and now my scheduling engine works in test for the first time in 5 years!