Example

This scenario was inspired by the teaching problem set out in the paper Describing behaviour of Processes with Many-to-Many Interactions by Dirk Fahland (2019).

Our example concerns the launch by Mission Control of a Space Vehicle. The Vehicle separates from its launcher and orbits the earth.

Mission Control dispatches Recovery Teams to locate both Vehicles after they have separately reentered the atmosphere.

Full source code is here.

DAGs

The net of the Vehicle proclet is simple; a linear arrangement of five transitions, terminating at the last.

    @property
    def dag(self):
        return {
            self.pro_launch: [self.pro_separation],
            self.pro_separation: [self.pro_orbit],
            self.pro_orbit: [self.pro_reentry],
            self.pro_reentry: [self.pro_recovery],
            self.pro_recovery: [],
        }

Recovery teams have three transitions, looping continually so that after having finished one job, they are available for another.

    @property
    def dag(self):
        return {
            self.pro_tasking: [self.pro_recovery],
            self.pro_recovery: [self.pro_standby],
            self.pro_standby: [self.pro_tasking],
        }

Mission Control is the most complex net. Some transitions loop back to themselves.

    @property
    def dag(self):
        return {
            self.pro_launch: [self.pro_separation],
            self.pro_separation: [self.pro_reentry],
            self.pro_reentry: [self.pro_recovery, self.pro_reentry],
            self.pro_recovery: [self.pro_recovery],
        }

Transitions

In Proclet theory, a Transition is activated when all its input places are occupied by tokens. It can choose whether to fire based on guard conditions. It may also block for a while, in the manner of Timed Petri Nets.

Here we implement each Transition as a single instance method of the Proclet object. It will be useful to adopt a small number of code patterns based on simple Python idiom.

Syncing

    def pro_launch(self, this, **kwargs):
        logging.info("We are go for launch", extra={"proclet": self})
        yield from self.channels["uplink"].send(
            sender=self.uid, group=self.group,
            action=this.__name__,
        )
        yield

Blocking

    def pro_launch(self, this, **kwargs):
        try:
            sync = next(
                i for i in self.channels["uplink"].receive(self, this)
                if i.action == this.__name__
            )
            logging.debug(sync, extra={"proclet": self})
        except StopIteration:
            return
        else:
            logging.info("Launch phase is complete", extra={"proclet": self})
            yield

Forking

    def pro_separation(self, this, **kwargs):
        logging.info("Separation initiated", extra={"proclet": self})
        v = Vehicle.create(
            name="Launch vehicle", orbits=None,
            channels={"beacon": self.channels["beacon"]}, group=self.group,
            marking=self.i_nodes[self.pro_reentry],
        )
        yield v
        yield from self.channels["uplink"].send(
            sender=self.uid, group=self.group, context={v.uid},
            action=this.__name__,
        )
        yield

Joining

    @property
    def recoveries(self):
        return [
            msgs[-1] for msgs in self.channels["vhf"].view(self.uid)
            if msgs[-1].action == Exit.deliver
        ]

Finishing

    def pro_recovery(self, this, **kwargs):
        recoveries = {i: r for r in self.recoveries for i in r.context}
        if len(recoveries) == 2:
            logging.info("Mission complete", extra={"proclet": self})
            raise Termination()

Output

Mission control|    pro_launch|We are go for launch
  Space vehicle|    pro_launch|Launch phase is complete
  Space vehicle|pro_separation|Separation initiated
Mission control|pro_separation|Copy your separation
 Launch vehicle|   pro_reentry|Re-entering atmosphere
  Space vehicle|     pro_orbit|In orbit 1
Mission control|   pro_reentry|Observing reentry of launch vehicle
  Space vehicle|     pro_orbit|In orbit 2
Mission control|  pro_recovery|Team 81a briefed for recovery of launch vehicle
  Space vehicle|     pro_orbit|In orbit 3
  Recovery Team|  pro_recovery|Commencing search for launch vehicle
  Recovery Team|  pro_recovery|Abandoning search for launch vehicle
  Space vehicle|   pro_reentry|Re-entering atmosphere
  Recovery Team|   pro_standby|Team 81a standing by
Mission control|   pro_reentry|Observing reentry of space vehicle
Mission control|  pro_recovery|Team 81a briefed for recovery of launch vehicle
  Recovery Team|  pro_recovery|Commencing search for launch vehicle
  Recovery Team|  pro_recovery|Rendezvous with launch vehicle
Mission control|  pro_recovery|Team 1f4 briefed for recovery of space vehicle
 Launch vehicle|  pro_recovery|Signing off
  Recovery Team|   pro_standby|Team 81a standing by
  Recovery Team|  pro_recovery|Commencing search for space vehicle
  Recovery Team|  pro_recovery|Abandoning search for space vehicle
Mission control|  pro_recovery|Team 81a briefed for recovery of space vehicle
  Recovery Team|   pro_standby|Team 1f4 standing by
  Recovery Team|  pro_recovery|Commencing search for space vehicle
  Recovery Team|  pro_recovery|Rendezvous with space vehicle
Mission control|  pro_recovery|Mission complete