Fork me on GitHub
#clara
<
2022-01-24
>
pdmct23:01:56

Hi, I am looking for some advice on how I implement some rules in my configurator. The issue I am looking to solve is — I want to connect n gadgets to a widget that has m gadget slots, where m > n. e.g. a widget may have 5 slots for gadgets but I only want to connect 3 gadgets according to some rules. I have been scratching my head how to do this using just rules, and relented to having some global state to manage this. This seems to do the trick, but is there a way to achieve the same/similar directly as rules without resorting to this approach? code goes something like this:

(def widget-connections (ref {1 [0 0 0 0 0]
                              2 [0 0 0 0 0]})) 

(defrecord Widget [id num-gadgets material])
(defrecord Wconn-avail [id from-id to-id status])

(defrecord Wconn-used [id from-id to-id status])
(defrecord GadgetA  [id on-widget]) 

(r/defrule add-gadget-a
  [?w <- Widget (= ?id id)]
  [:not [GadgetA (false? id)]]
  => 
  (doseq [gid (range (:num-gadgets ?w))]
    (r/insert! (->GadgetA gid
                          ?id))))

(r/defrule connect-gadget-a
  [?w <- Widget (= ?id id)]
  [?g <- GadgetA (= ?gid id) (= ?id on-widget)]
  [?c <- Wconn-avail (= ?cid id) (= ?id from-id) (= ?status status "unused")]
  [:not [Wconn-used (false? id)]]
  => 
  (dosync
   (let [avail-conns (get @widget-connections ?id)         
         this-conn (first (shuffle (filter #(= 0 (second %)) (map-indexed vector avail-conns))))]
         (r/insert! (->Wconn-used (first this-conn)
                                  ?id
                                  ?gid
                                  "connected"))
         (alter widget-connections (fn [a]
                                     (assoc a
                                            ?id
                                            (assoc (get a ?id) (first this-conn) 1)))))))

(def session (-> (r/mk-session 'my.widget)
                 (r/insert  (->Widget 1 3 "gold"))
                 (r/insert  (->Widget 2 2 "silver"))
                 (r/insert (->Wconn-avail 0 1 nil "unused"))
                 (r/insert (->Wconn-avail 0 2 nil "unused"))
                 (r/fire-rules)))

mikerod19:01:30

What sort of results are you looking to query? I think I could help better if I understood more what the expected sort of query output would be from this. @U066S7PQC

mikerod19:01:21

I am thinking the widget-connections state is actually what you’d want be modeling as a fact instead that you query out from the session after r/fire-rules?

pdmct22:01:22

The output that I am looking for is a hierarchal product config. something like:

Widget A1
 - Slot 1 zloc:0: Gadget A 1
 - Slot 2 zloc:1: <empty>
 - Slot 3 zloc:3: <empty>
 - Slot 4 zloc:4: Gadget A 2
 - Slot 5 zloc:6: <empty> 
 - Slot 6 zloc:8: Gadget A 3
 - Slot 7 zloc:10: <empty>
I am using the rule to generate a variety of different product configurations (obviously a bit more complex than this) that comply with a set of rules. The main issue is the choice of which ‘slot’ to connect sub-components too, as the rules require knowledge of the other ‘sub-components’ . As an example, the sample rules would be: connect 3 gadgets, fill from the top with >= 4 z_location between connected gadgets of a particular type (eg x->A) etc, with a >= 3 z_location between connected gadgets with a different type say x->B. So a config with an ABB gadget set might connect like:
Widget A1
 - Slot 1 zloc:0: Gadget A 1
 - Slot 2 zloc:1: <empty>
 - Slot 3 zloc:3: Gadget B 2
 - Slot 4 zloc:4: <empty>
 - Slot 5 zloc:6: Gadget B 3
 - Slot 6 zloc:8: <empty> 
 - Slot 7 zloc:10: <empty>

pdmct05:01:38

probably just needed a little prod, and reading the doco a little more closely, so modelling the connection list directly something like this:

(defrecord Widget-conns [id conn-list])

(r/defrule connect-gadget-a
  [?w <- Widget (= ?id id)  (= ?num-gadgets num-gadgets)]
  [?c <- (acc/all) :from [GadgetA (= ?id on-widget) ]]
  [:not [Widget-conns (false? id)]]
  [:test (> (count ?c) ?num-gadgets)]
  =>
  (println (str "building connection list for " ?w " ie. " (vec ?c)))
  (let [id-list (mapv :id ?c)]
    (r/insert! (->Widget-conns 0
                             (shuffle (first (partition 5 5 (repeat -1) id-list)))))))
giving:
(r/query session widget-conns?)
({:?w {:id 0, :conn-list [-1 1 0 -1 2]}})
this seems to do the trick

mikerod02:01:45

I think this is looking better! I’m sorry I was preoccupied and couldn’t look closer yet.

mikerod02:01:01

However, you seem to be tracking the correct way now. Usually the answer is to just model more of the concepts directly as facts.

👍 1
pdmct03:02:08

Thanks for the help

👍 1
mikerod03:02:36

Glad you made progress.