SRRC Wiki

Service Robotics Research Center
at Ulm University of Applied Sciences

User Tools

Site Tools


sdvg:details_tcl_feature

Details on the TCL Feature

This feature allows you to program the behavior of service robots. It is an implementation of the LISP-based DSL SmartTCL with the Eclipse Modeling Tools. In contrast to the more general Building Block Feature, this allows you to program concrete behavior for simulated or real robots due to its close integration with the SmartSoft Approach. Furthermore, a first simple implementation exists that allows to define relations from a TCL model to a Building Block model and a DVG model to have an automatic support for the evaluation process and the resulting configuration step.

Note that not all language constructs from LISP were already mapped to this DSL. Therefore, it is still sometimes required to program in LISP when you use this DSL.

General Hints

The Palette of the graphical representation in Sirius is not complete. Hence, defining the behavior purely graphically is not possible. It is recommended to program the behavior using the textual Xtext model and to create the graphical representation afterwards to get a better overview about the different types of blocks and their relationships.

In the context of an action you can write LISP code inside quotation marks. You can combine it with other commands available in the DSL. Some commands can also be nested with each other. Such a CompositeInstruction must start with < and end with >. Inside a CompositeInstruction can be used:

  • LISP Code (Strings inside quotation marks)
  • References to variables (with $nameOfTheVariable)
  • The command KBQuery
  • The command KBQueryAll
  • The command KBUpdate
  • The command KBDelete
  • The command GetValue

Transportation Example

In general, this behavior realizes the Transportation Building Block. However, an explicit mapping between both models is only done in the Details on the TCL Feature next example. For this example, we only want to focus on how to program concrete behavior.

The complete project can be found here.

Defining the Knowledge Base of a Robot

In the following we describe how the knowledge base of a robot can be defined. We can define this in a separate Behavior in a different file *.tcl with respect to the Behavior that is described in the next section in order to keep a better overview.

  Behavior KB { 
    
    KBIsAGroupCollection {  
	    
	    KBIsAGroup robot { 
		    name isKey true,
		    "current-room" isKey false,
		    goalid isKey false,
		    currentSymbolicPosition isKey false,
		    "velocity-travelling" isKey false,
		    position isKey false
	    },
	    
	    KBIsAGroup location { 
		    name isKey true,
		    speech isKey false,
		    approachType isKey false, 
		    "approach-region-pose" isKey false,
		    "approach-region-dist" isKey false,
		    approachExactPose isKey false,
		    approachExactDist isKey false,
		    approachExactSafetyCl isKey false,
		    orientationRegion isKey false,
		    orientationExact isKey false, 
		    backwardDist isKey false  
	    },
	    
	    KBIsAGroup counter {  
		    name isKey true,
		    count isKey false
	    },
	    
	    KBIsAGroup room {
		    name isKey true,
		    size isKey false,
		    offset isKey false
	    },
	    
	    KBIsAGroup station { 
		    id isKey true,
		    type isKey false,
		    "approach-location" isKey false   
	    }		 
    }

KBIsAGroupCollection is a collection of different KBIsAGroup definitions. A KBIsAGroup is in turn a collection of relevant attributes to which values can be assigned later. After defining the name of an attribute, it must follow whether this attribute is a key or not. The set of attributes of a KBIsAGroup which are keys uniquely identify the tuples that are added to the knowledge base. In the example above, we define among other things that there is a KBIsAGroup named robot which has the attributes name, current-room1), goalid, currentSymbolicPosition, velocity-travelling and position. Only name is an attribute that serves as a key. But note that the KBIsAGroup itself always automatically serves as a key. Hence robot in combination with name uniquely identifies the tuples.

    InitialKnowledge {
	    KBUpdate { 
		    is-a = "KB.robot",
		    "KB.robot.name" = <"1">,
		    "KB.robot.velocity-travelling" = <"((0 500)(-60 60))">
		    with-keys (is-a, "KB.robot.name")
	    }
    } 
  }

In InitialKnowledge we can assign values to the KBIsAGroup definitions from above that are known initially. This can be done by the KBUpdate command. In the example above, the tuple added to the knowledge base is a robot with name 1 and a corresponding velocity-travelling. Finally, we need to specify the list of keys according to the referenced KBIsAGroup robot. is-a is mandatory here because it always serves as a key.

Defining the Behavior of a Robot

Graphical Model

Textual Model

Behavior Transportation {      
  
  ErrorCollection { 
	  UnknownLocation = "(ERROR (UNKNOWN LOCATION))",
	  RobotBlocked = "(ERROR (ROBOT BLOCKED))"  
  }

We define a list of errors with corresponding LISP strings as values that can be later returned in TCBs if the corresponding errors occur.

  TCB NAME DeliverFromToMpsStation (fetchStationId, fetchBeltId, deliverStationId, deliverBeltId) { 
	  push-plan (FetchFromMpsStation <$fetchStationId $fetchBeltId>, 
		         DeliverToMpsStation <$deliverStationId $deliverBeltId>
	  )
  }

We define a TCB named DeliverFromToMpsStation which takes fetchStationId, fetchBeltId, deliverStationId and deliverBeltId as inputs. This TCB pushes the TCBs FetchFromMpsStation and DeliverToMpsStation to the execution stack and passes the corresponding inputs to them from its own inputs. The $ symbol is used to reference existing variables. The NAME after TCB indicates that the name that follows NAME is the name that should be used when generating the TCB in SmartTCL. 2) 3)

  TCB NAME FetchFromMpsStation (stationId, beltId) {
	  actions {
		  Define {
			  station = <KBQuery { is-a "KB.station" "KB.station.id" = <$stationId> }>
			  stationType = <GetValue(<$station>, "KB.station.type")>
			  stationApproachLocation = <GetValue(<$station>, "KB.station.approach-location")>
			  
			  actions {
				  push-plan (Navigation.MoveRobot <$stationApproachLocation>, 
						     MPS.MpsStationDock <$stationId $beltId>,
						     MPS.MpsStationLoad <$stationId>,
						     MPS.MpsStationUndock 
				  )
			  }
		  }
	  }
  }

Here is the definition of the TCB FetchFromMpsStation which is pushed to the stack in the DeliverFromToMpsStation TCB. It contains an action in which a Define is used to specify the scope for variables. We first assign to the variable station the result of the KBQuery command which wants to know the tuple of the KBIsAGroup station with the name stationId (the input of this TCB). Next, we assign to the variable stationType, the value of the attribute type from the tuple that was queried one line above and which was stored in the station variable. For this purpose, we use the GetValue command. In the same way, we then assign to the variable stationApproachLocation, the value of the attribute approach-location from the station variable. After the variable assignments, an action follows in which we can access these variables. In the action we push a plan of other TCBs to the execution stack. We pass the stationApproachLocation variable to the MoveRobot TCB that needs the prefix Navigation because it belongs to the Navigation module. The other TCBs need the prefix MPS because they belong to the MPS module.

  TCB NAME DeliverToMpsStation (stationId, beltId) {
	  actions {
		  Define {
			  station = <KBQuery { is-a "KB.station" "KB.station.id" = <$stationId> }> 
			  stationType = <GetValue(<$station>, "KB.station.type")>
			  stationApproachLocation = <GetValue(<$station>, "KB.station.approach-location")>
			  
			  actions {
				  push-plan (Navigation.MoveRobot <$stationApproachLocation>,
						     MPS.MpsStationDock <$stationId $beltId>,
						     MPS.MpsStationUnload <$stationId>,
						     MPS.MpsStationUndock
				  )
			  }
		  }
	  }		
  } 

Here is the definition of the TCB DeliverToMpsStation which is pushed to the stack in the DeliverFromToMpsStation TCB. Its action is same to the previous FetchFromMpsStation TCB. The only difference is that it pushes MpsStationUnload instead of MpsStationLoad.

TCB NAME [NavigationModule] MoveRobot subname ApproachRegion (location) {     
	actions { 
		Define {
			pose = <GetValue(<KBQuery { is-a "KB.location" "KB.location.name" = <$location> } >,"KB.location.approach-region-pose")>
			dist = <GetValue(<KBQuery { is-a "KB.location" "KB.location.name" = <$location> } >,"KB.location.approach-region-dist")>
			robotCurrentRoomName = <GetValue(< KBQuery { is-a "KB.robot" } >,"KB.robot.current-room")>
			robotRoom = <KBQuery { is-a "KB.room" "KB.room.name" = <$robotCurrentRoomName> }>
			velocityTravelling = <GetValue(< KBQuery { is-a "KB.robot" } >, "KB.robot.velocity-travelling")> 
			goalid = <"nil">
			
			actions {   
				
				if <"(null" $pose")"> {   
					return UnknownLocation
				}
			   	else {

In case the pose is not null:

					EventActivation { evtName "evt-cdlgoal" server "cdl" service "goalEvent" eventMode CONTINUOUS eventhandler Cdl }	
					EventActivation { evtName "evt-cdlblocked" server "cdl" service "blockedEvent" eventMode CONTINUOUS eventhandler CdlBlocked }
					EventActivation { evtName "evt-planner" server "planner" service "plannerEvent" eventMode CONTINUOUS eventhandler PlannerApproachHalt }

We activate three events with corresponding handlers whose definitions can be found further down.

					KBUpdate { 
						is-a = "KB.counter",
					        "KB.counter.name" = <"no-path-counter">,
						"KB.counter.count" = <"0">	   
						with-keys (is-a)        
					}  						
                                        "(setf" $goalid GetValue(<KBQuery { is-a "KB.robot" } >,"KB.robot.goalid")")"
	                                "(setf" $goalid "(+" $goalid "1))"
	                                "(format t \"GoalID: ~s~%\" "$goalid")"
					KBUpdate {
						is-a = "KB.robot",
						"KB.robot.goalid" = <$goalid>
						with-keys (is-a)
					}

We update some information in the knowledge base.

					State { server "planner" state "Neutral" } 
					State { server "mapper" state "Neutral" }

We set the State of the planner and mapper component to Neutral so that we can parameterize them in the following.

					Param { server "cdl" slot "CommNavigationObjects.CdlParameter.ID" value <$goalid>}
					Param { server "cdl" slot "CommNavigationObjects.CdlParameter.LOOKUPTABLE" value <"'DEFAULT">}
			   		Param { server "cdl" slot "CommNavigationObjects.CdlParameter.SAFETYCL" value <"200">}
			   		Param { server "cdl" slot "CommNavigationObjects.CdlParameter.TRANSVEL" value <"(first" $velocityTravelling")">}
					Param { server "cdl" slot "CommNavigationObjects.CdlParameter.ROTVEL" value <"(second" $velocityTravelling")">}				   		
					Param { server "cdl" slot "CommNavigationObjects.CdlParameter.APPROACHDIST" value <$dist>}
					Param { server "cdl" slot "COMMIT"}
					Param { 
							server "mapper" slot "CommNavigationObjects.MapperParams.CURPARAMETER" 
						    value <"(append" GetValue(<$robotRoom>,"KB.room.size") GetValue(<$robotRoom>,"KB.room.offset") "(list" $goalid"))"> 
					}

					Param { server "mapper" slot "CommNavigationObjects.MapperParams.CURLOADLTM" }
					Param { server "mapper" slot "COMMIT" }
					
					Param { server "planner" slot "CommNavigationObjects.PlannerParams.ID" value <$goalid>}
					Param { server "planner" slot "CommNavigationObjects.PlannerParams.DELETEGOAL" }
					Param { server "planner" slot "CommNavigationObjects.PlannerParams.SETDESTINATIONCIRCLE" value <"`(,(first" $pose") ,(second" $pose") ,"$dist")">}
					Param { server "planner" slot "COMMIT" }

We parameterize the cdl, mapper and planner component. For example, we send to the planner the goal location via SETDESTINATIONCIRCLE.

					State { server "planner" state "PathPlanning" }
					State { server "mapper" state "BuildCurrMap" } 

We set the State of the planner component to PathPlanning so that it starts to plan a path to the goal location. The State of the mapper is set to BuildCurrMap so that it starts to build a map.

					return Success						   	
				}					
			} 
		}
	}
}
EventHandler NAME Cdl { 
	actions {
		if <"(equal (tcl-event-message) \"(reached)\")"> {
			"(format t \"=========================>>> GOAL REACHED !!! ~%\")"
			State { server "cdl" state "Neutral" }
			State { server "mapper" state "Neutral" }
			State { server "planner" state "Neutral" }
			KBUpdate {
				is-a = "KB.robot",
				"KB.robot.currentSymbolicPosition" = <$"MoveRobot.location">
				with-keys (is-a)
			}
			Abort
			return Success
		}
	}
}

In case that the cdl component sends the event reached, we set the States of the components cdl, mapper and planner to Neutral.

EventHandler NAME CdlBlocked { 
	actions {
		if <"(equal (tcl-event-message) \"(blocked)\")"> {
			"(format t \"=========================>>> ROBOT BLOCKED !!! ~%\")"
			State { server "cdl" state "Neutral" }
			State { server "mapper" state "Neutral" }
			State { server "planner" state "Neutral" }
			Abort
			return RobotBlocked		
		}
	}
}

In case that the cdl component sends the event blocked, we set the States of the components cdl, mapper and planner to Neutral.

EventHandler NAME PlannerApproachHalt { 
	actions {
		if <"(equal (tcl-event-message) \"(start occupied by goal)\")"> {
			"(format t \"=========================>>> start occupied by goal !!! ~%\")"
			State { server "mapper" state "Neutral" }
			State { server "planner" state "Neutral" }
			KBUpdate {
				is-a = "KB.robot",
				"KB.robot.currentSymbolicPosition" = <$"MoveRobot.location">
				with-keys (is-a)
			}            		
			Abort
			return Success
		}
		else-if <"(equal (tcl-event-message) \"(start occupied by obstacle)\")"> {
			"(format t \"=========================>>> start occupied by obstacle !!! ~%\")"
		   	Param { server "mapper" slot "CommNavigationObjects.MapperParams.CURLOADLTM" }	
		}
		else-if <"(equal (tcl-event-message) \"(wrong map id)\")"> {
			"(format t \"=========================>>> wrong map id !!! ~%\")"
		} 
		else-if <"(equal (tcl-event-message) \"(no path)\")"> { 
			Define {
				counterClass = <KBQuery { is-a "KB.counter" "KB.counter.name" = <"no-path-counter">  }>
				counter = <GetValue(<$counterClass>,"KB.counter.count")> 
				actions {
					"(setf counter  (+ counter 1))
          		 	(format t \"=========================>>> no path !!! count: ~a ~%\" counter)
          		 	"
					KBUpdate { 
						is-a = "KB.counter",
					    "KB.counter.name" = <"no-path-counter">,
						"KB.counter.count" = <",counter">
						with-keys (is-a)	           
					} 
					if <"(<" $counter "3)"> {
						"(format t \"Clean current map! ~%\")"
						Param { server "mapper" slot "CommNavigationObjects.MapperParams.CURLOADLTM" }	
					}
					else {
						State { server "cdl" state "Neutral" }
				   		State { server "purepursuit" state "Neutral" }
				   		State { server "mapper" state "Neutral" }
				   		State { server "planner" state "Deactivated" }
				   		State { server "planner" state "Neutral" }
				   		Abort
				   		return RobotBlocked
					}	
				}
			}	
		}
		else-if <"(equal (tcl-event-message) \"(ok)\")"> {
			"(format t \"=========================>>> ok !!! ~%\")"
	   		Param { server "cdl" slot "CommNavigationObjects.CdlParameter.GOALMODE" value <"'PLANNER"> }
			Param { server "cdl" slot "CommNavigationObjects.CdlParameter.LOOKUPTABLE" value <"'DEFAULT"> }
			Param { server "cdl" slot "CommNavigationObjects.CdlParameter.SETSTRATEGY" value <"'APPROACH_HALT"> }			   		
			Param { server "cdl" slot "COMMIT" }
			State { server "cdl" state "MoveRobot" }	
		}
		else {
			"(format t \"=========================>>> unsupported event ~s !!! ~%\" *MSG* )"
		}
	}
}

We do some error handling if the planner sends an event that is not ok. But if the returned event is ok, then we parameterize the cdl component responsible for motion control and finally set its State to MoveRobot so that the robot starts moving.

TCB NAME [MPSModule] MpsStationDock (stationId, beltId) {
	actions {
		EventActivation { evtName "evtMpsDocking" server "MPSDOCKING" service "dockingevent" eventMode CONTINUOUS eventhandler MpsDocking }
		State { server "MPSDOCKING" state "LaserDocking" }
	}
}

EventHandler NAME MpsDocking {
	actions {
		if <"(equal (tcl-event-message) \"(laser docking not done)\")"> {
			"(format t \"=========================>>> LASER DOCKING DOCKING START~%\")"	
		}
		else-if <"(equal (tcl-event-message) \"(laser docking done)\")"> {
			"(format t \"=========================>>> LASER DOCKING DOCKING DONE~%\")"
			State { server "MPSDOCKING" state "Neutral" }
			Abort
			return Success
		}
		else-if <"(equal (tcl-event-message) \"(undocking not done)\")"> {
			"(format t \"=========================>>> UNDOCKING DOCKING START~%\")"	
		}
		else-if <"(equal (tcl-event-message) \"(undocking done)\")"> {
			"(format t \"=========================>>> UNDOCKING DOCKING DONE~%\")"	
			State { server "MPSDOCKING" state "Neutral" }
			Abort
			return Success
		}	
	}	
}

TCB NAME [MPSModule] MpsStationLoad (stationId) {
	actions {
		EventActivation { evtName "evtBeltLoading" server "BELT" service "loadevent" eventMode CONTINUOUS eventhandler MpsLoading }
		Param { server "BELT" slot "COMMROBOTINOOBJECTS.ROBOTINOCONVEYERPARAMETER.SETSTATIONID" value <$stationId> }
		Param { server "BELT" slot "COMMIT" }
		State { server "BELT" state "load" }
	}
}

EventHandler NAME MpsLoading {
	actions {
		if <"(equal (tcl-event-message) \"(load not done)\")"> {
			"(format t \"=========================>>> load START~%\")"	
		}
		else-if <"(equal (tcl-event-message) \"(load done)\")"> {
			"(format t \"=========================>>> load DONE SUCCESS~%\")"
			State { server "BELT" state "Neutral" }
			DeletePlan
			Abort
			return Success
		}
		else-if <"(equal (tcl-event-message) \"(load error no box)\")"> {
			"(format t \"=========================>>> load DONE ERROR (no box)~%\")"
		}
		else-if <"(equal (tcl-event-message) \"(load error box already present)\")"> {
			"(format t \"=========================>>> load DONE ERROR (box already present)~%\")"				
		}
		else-if <"(equal (tcl-event-message) \"(load error no response from station)\")"> {
			"(format t \"=========================>>> load DONE ERROR (no response from station)~%\")"				
		}	
		else-if <"(equal (tcl-event-message) \"(unload not done)\")"> { 
			"(format t \"=========================>>> unload START~%\")"	
		}
		else-if <"(equal (tcl-event-message) \"(unload done)\")"> { 
			"(format t \"=========================>>> unload DONE SUCCESS~%\")"
			State { server "BELT" state "Neutral" }
			DeletePlan
			Abort
			return Success
		}	
		else-if <"(equal (tcl-event-message) \"(unload error no box)\")"> {
			"(format t \"=========================>>> unload DONE ERROR (no box)~%\")"
		}	
		else-if <"(equal (tcl-event-message) \"(unload error box still present)\")"> {
			"(format t \"=========================>>> unload DONE ERROR (box still present)~%\")"
		}	
		else-if <"(equal (tcl-event-message) \"(unload error no response from station)\")"> {
			"(format t \"=========================>>> unload DONE ERROR (no response from station)~%\")"
		}														
	}		
}

TCB NAME [MPSModule] MpsStationUnload (stationId) {   
	actions {
		EventActivation { evtName "evtBeltUnloading" server "BELT" service "loadevent" eventMode CONTINUOUS eventhandler MpsLoading }
		Param { server "BELT" slot "COMMROBOTINOOBJECTS.ROBOTINOCONVEYERPARAMETER.SETSTATIONID" value <$stationId> }
		Param { server "BELT" slot "COMMIT" }
		State { server "BELT" state "unload" }	
	}
}
 
TCB NAME [MPSModule] MpsStationUndock {  
	actions {
		EventActivation { evtName "evtMpsDocking" server "MPSDOCKING" service "dockingevent" eventMode CONTINUOUS eventhandler MpsDocking }
		State { server "MPSDOCKING" state "UnDocking" }	
	}
}

We do some interactions with the corresponding components via Param, State and event handlers to realize docking, loading, unloading and undocking respectively.

Usage

Put the generated *.smartTcl files to the behaviorFiles folder in your SmartSoft System Deployment and make sure that unique TCB signatures are used across all files.

Transportation Example with Relations to Building Block and DVG Models

In this example, we extend the previous example by adding explicit relations to a Transportation Building Block model and its associated DVG model in order to link this implementation to the evaluation process. The complete projects can be found here (TCL) 4) and here (Building Block+DVG) 5).

BehaviorTransportation.tcl

Behavior Transportation {       

 Import ProvideFunctions
 Import ConfigureFunctions 

We import two other files named ProvideFunctions.tcl and ConfigureFunctions.tcl. The content and purpose of these files is explained later.

TCB NAME DeliverFromToMpsStation (fetchStationId, fetchBeltId, deliverStationId, deliverBeltId)  
	realizes "Transportation.Transportation" 
	RequirementSpecification true  
	{ 
		push-plan (0 : FetchFromMpsStation <$fetchStationId $fetchBeltId>, 
			       1 : DeliverToMpsStation <$deliverStationId $deliverBeltId>
		)
	}

We now define that DeliverFromToMpsStation realizes the building block Transportation and we set RequirementSpecification to true. This means that this TCB should be parameterizable with respect to the requirements that are specifiable in the associated DVG model. We call this TCB the Requirements-TCB in the following.

TCB NAME [NavigationModule] MoveRobot_ApproachRegion subname ApproachRegion (location) realizes "BBRepo.GotoLocation" {     
	actions { 
		Define {
			pose = <GetValue(<KBQuery { is-a "KB.location" "KB.location.name" = <$location> } >,"KB.location.approach-region-pose")>
			dist = <GetValue(<KBQuery { is-a "KB.location" "KB.location.name" = <$location> } >,"KB.location.approach-region-dist")>
			robotCurrentRoomName = <GetValue(< KBQuery { is-a "KB.robot" } >,"KB.robot.current-room")>
			robotRoom = <KBQuery { is-a "KB.room" "KB.room.name" = <$robotCurrentRoomName> }>
			velocityTravelling = <GetValue(< KBQuery { is-a "KB.robot" } >, "KB.robot.velocity-travelling")> 
			goalid = <"nil">
			actions {   
					if <"(null" $pose")"> {   
						return UnknownLocation
					}
				   	else {  
						EventActivation { evtName "evt-cdlgoal" server "cdl" service "goalEvent" eventMode CONTINUOUS eventhandler Cdl }	
						EventActivation { evtName "evt-cdlblocked" server "cdl" service "blockedEvent" eventMode CONTINUOUS eventhandler CdlBlocked }
						KBUpdate { 
							is-a = "KB.counter",
						    "KB.counter.name" = <"no-path-counter">,
							"KB.counter.count" = <"0">	   
							with-keys (is-a)        
						}  
						EventActivation { evtName "evt-planner" server "planner" service "plannerEvent" eventMode CONTINUOUS eventhandler PlannerApproachHalt }
						"(setf" $goalid GetValue(<KBQuery { is-a "KB.robot" } >,"KB.robot.goalid")")"
		                                "(setf" $goalid "(+" $goalid "1))"
		                                "(format t \"GoalID: ~s~%\" "$goalid")"
						KBUpdate {
							is-a = "KB.robot",
							"KB.robot.goalid" = <$goalid>
							with-keys (is-a)
						}
						State { server "planner" state "Neutral" } 
						State { server "mapper" state "Neutral" }
						
						Param { server "cdl" slot "CommNavigationObjects.CdlParameter.ID" value <$goalid>}
						Param { server "cdl" slot "CommNavigationObjects.CdlParameter.LOOKUPTABLE" value <"'DEFAULT">}
				   		Param { server "cdl" slot "CommNavigationObjects.CdlParameter.SAFETYCL" value <"200">}
				   		//Param { server "cdl" slot "CommNavigationObjects.CdlParameter.TRANSVEL" value <"(first" $velocityTravelling")">}
						Param { server "cdl" slot "CommNavigationObjects.CdlParameter.ROTVEL" value <"(second" $velocityTravelling")">}				   		
						Param { server "cdl" slot "CommNavigationObjects.CdlParameter.APPROACHDIST" value <$dist>}
						Param { server "cdl" slot "COMMIT"}
						Param { 
						   server "mapper" slot "CommNavigationObjects.MapperParams.CURPARAMETER" 
					           value <"(append" GetValue(<$robotRoom>,"KB.room.size") GetValue(<$robotRoom>,"KB.room.offset") "(list" $goalid"))"> 
						}
						Param { server "mapper" slot "CommNavigationObjects.MapperParams.CURLOADLTM" }
						Param { server "mapper" slot "COMMIT" }
						
						Param { server "planner" slot "CommNavigationObjects.PlannerParams.ID" value <$goalid>}
						Param { server "planner" slot "CommNavigationObjects.PlannerParams.DELETEGOAL" }
						Param { server "planner" slot "CommNavigationObjects.PlannerParams.SETDESTINATIONCIRCLE" value <"`(,(first" $pose") ,(second" $pose") ,"$dist")">}
						Param { server "planner" slot "COMMIT" }
						
						State { server "planner" state "PathPlanning" }
						State { server "mapper" state "BuildCurrMap" } 
						
						return Success						   	
				   }					
			} 
		}
	}
}

We define that MoveRobot_ApproachRegion realizes the building block GotoLocation. Furthermore, we commented the line that sends the hardcoded TRANSVEL via Param to the cdl component because this is a variability entity whose value is determined by the solver in dependence of the requirements.

The remaining part of the model is not shown here, because it is same to the previous example. You only need to define realize relationships to building blocks for TCBs to which you want to formulate requirements or which should configure relevant variability entities according to the models.

ProvideFunctions.tcl

The purpose of this file which is imported by the Transportation Behavior is to define the Functions that return the information that the DVG model requires as input.

Behavior ProvideFunctions {         
	Function NAME GetFromStationLocation (stationId ("Transportation.DeliverFromToMpsStation.fetchStationId")) provides "Transportation.INITFetchStation.FetchStation" { 
		actions {
			Define {
				station = <KBQuery { is-a "KB.station" "KB.station.id" = <$stationId> }>
				stationType = <GetValue(<$station>, "KB.station.type")>
				stationApproachLocation = <GetValue(<$station>, "KB.station.approach-location")>
				location = <GetValue (<KBQuery { is-a "KB.location" "KB.location.name" = <$stationApproachLocation> }>, "KB.location.approach-region-pose")>
				actions {
					"(setf result" $location ")" 
				}
			}	 
		}	
	}     
	Function NAME GetToStationLocation (stationId ("Transportation.DeliverFromToMpsStation.deliverStationId")) provides "Transportation.INITDeliverStation.DeliverStation" { 
		actions {
			Define {
				station = <KBQuery { is-a "KB.station" "KB.station.id" = <$stationId> }>
				stationType = <GetValue(<$station>, "KB.station.type")>
				stationApproachLocation = <GetValue(<$station>, "KB.station.approach-location")>
				location = <GetValue (<KBQuery { is-a "KB.location" "KB.location.name" = <$stationApproachLocation> }>, "KB.location.approach-region-pose")>
				actions {
					"(setf result" $location ")" 
				}
			}	 
		}	
	}

We define two Functions which return the FetchStation and the DeliverStation information for the DVG model by defining the corresponding references via provides. Since the input of these two Functions stationId depends on the inputs of the DeliverFromToMpsStation TCB to which we want to specify our requirements, we define the references accordingly. This means: The stationId input for GetFromStationLocation is the fetchStationId from DeliverFromToMpsStation and the stationId input for GetToStationLocation is the deliverStationId from DeliverFromToMpsStation. In the Functions we query the knowledge base and return the associated approach-region-pose for the corresponding station.

	TCB NAME GetCurrentRobotPosition provides "Transportation.INITStartPosition.StartPosition" {
		actions {
			push-plan ("BASE".getBasePose <"=> ?x ?y ?yaw">,
				   WriteCurrentRobotPositionToKB <"?x ?y ?yaw">
			)
		}
	}
	TCB IGNORE getBasePose [getBasePose] (xp, yp, yawp) {
		
	} 	
	TCB NAME WriteCurrentRobotPositionToKB (xp, yp, yawp) {   
		actions {
			"(setf pos (list " $xp $yp $yawp "))"
			KBUpdate { 
				is-a = "KB.robot",
				"KB.robot.position" = <",pos">
				with-keys (is-a)
			}
		}
	}
	Function NAME GetCurrentRobotPositionFromKB provides "Transportation.INITStartPosition.StartPosition" {
		actions {
			GetValue (<KBQuery { is-a "KB.robot" }>, "KB.robot.position")
		}
	}
} 

This part is required to return the current position of the robot which serves as the StartPosition information in the DVG model. To realize that, we need to take a little detour due to certain peculiarities of SmartTCL. First we need to define a Helper-TCB which gets the pose from the base component via getBasePose. We use IGNORE for this TCB because it is already available in the corresponding components (for example here). Then we write the information to the knowledge base. Finally, the providing Function GetCurrentRobotPositionFromKB returns the corresponding position by querying the knowledge base.

ConfigureFunctions.tcl

The purpose of this file which is imported by the Transportation Behavior is to define the Functions that configure the values that the solver of the Building Block/DVG model determined in dependence of the inputs (i.e. the information of the ProvideFunctions and the requirement specification). Although we call it Functions, we actually have to use TCBs for configuration in order to be able to use Param.

Behavior ConfigureFunctions {    
	TCB NAME [NavigationModule] setVelocity (externalValue) configures "BBRepo.GotoLocation.Velocity" for "Transportation.MoveRobot_ApproachRegion" callPrefix Navigation {
		actions {
			Param { server "cdl" slot "CommNavigationObjects.CdlParameter.TRANSVEL" value <"`(0"$externalValue")">}
			Param { server "cdl" slot "COMMIT"}
		}
	}	
}  

In this example, only one TCB is required which sets the Velocity of the GotoLocation building block for the MoveRobot_ApproachRegion TCB.

Result (General)

The code generator will ensure the following:

  • The Requirements-TCB is parameterizable regarding the requirements of the associated DVG model.
  • The values of the commanded requirements are written to a file named dataForSolver.txt before the child-TCBs of the Requirements-TCB are called.
  • All ProvideFunctions are called and written to a file named dataForSolver.txt before the child-TCBs of the Requirements-TCB are called.
  • The solver is executed, which reads the dataForSolver.txt file and determines the result before the child-TCBs of the Requirements-TCB are called.
  • The result is read from a file named dvgConfiguration.json before the child-TCBs of the Requirements-TCB are called.
  • The ConfigureFunctions are called before a TCB is called for which a relevant value binding was determined by the solver.

In order for it to work, it is important that in the associated DVG model, the INTERFACE of the InitPorts that should read from the dataForSolver.txt is set to EXTERNAL. Otherwise, the solver will read the internal values initialized in the DVG model. Furthermore, ports representing variability entities of different building block instances should assign a corresponding Index so that the association on the TCL side works.

Result (Example)

For our example, this means that the generated TCB of our Requirements-TCB will look as following:

(define-tcb (DeliverFromToMpsStation ?fetchStationId ?fetchBeltId ?deliverStationId ?deliverBeltId ?TimeConstraint)...

A TimeConstraint (in seconds) can be passed to the TCB because the associated DVG model has a corresponding InitCPort. The INTERFACE of this InitCPort must be EXTERNAL same as for the InitPorts FetchStation, DeliverStation and StartPosition. Furthermore we assigned Velocity1 an Index of 0 and Velocity2 an Index of 1 in the DVG model.

Usage

  • Put the generated *.smartTcl files to the behaviorFiles folder in your SmartSoft System Deployment and make sure that unique TCB signatures are used across all files.
  • Put the compiled solver (the java binaries) with the correct INTERFACE assignments to the ComponentTCL_Sequencer_data folder in your SmartSoft System Deployment or compile the java source file in this directory.
  • Command DeliverFromToMpsStation in the prompt of the ComponentTCL_Sequencer component with the desired values. For example: (execute '(DeliverFromToMpsStation 0 1 1 1 100)) to say that the robot should do the transportation task from station 0 to station 1 within 100 seconds.
1)
General hint: If you want to use names in Xtext that contain the minus character (-), then you have to put the whole name in quotation marks.
2)
An alternative is EXTERNALNAME: TCB EXTERNALNAME YourSelectedName [AnExternalName] in order to use AnExternalName for the TCB when generating the TCB in SmartTCL.
3)
An alternative is IGNORE: TCB IGNORE YourSelectedName in order to not generate this TCB in SmartTCL at all. (This can be helpful if such a TCB already exists in SmartTCL and you do not want to reimplement it. But then you have at least the option to define the signature of this TCB and you are able to reference it from other TCBs.)
4)
Note that this is a different project than the project that was explained in the previous section because the models slightly differ.
5)
Note that this is a different project than the project that is explained here because the models slightly differ.
sdvg/details_tcl_feature.txt · Last modified: 2023/02/20 08:14 by admin

DokuWiki Appliance - Powered by TurnKey Linux