I have a bot code that needs to be connected to a state machine, how to implement it correctly, please help me, I understand in theory how to do this, but in practice, it does not work
states.py
from transitions import Machine, State
from main import *
states = ['START',
'WAITING_YES',
'CHOOSE SIZE',
'GO PAY'
'CHOOSE PAY METHOD',
'REPEATING ORDER',
'FINISH']
machine = Machine(states=states, initial='START')
machine.add_transition('melt', source='START', dest='WAITING_YES')
if start:
print('-------------------',machine.state,'-------------------')
machine.add_ordered_transitions()
if waiting_yes:
machine.to_WAITING_YES()
print('-------------------',machine.state,'-------------------')
machine.next_state()
elif choose_size:
print('-------------------',machine.state,'-------------------')
machine.next_state()
elif choose_pay_method:
print('-------------------',machine.state,'-------------------')
machine.next_state()
elif repeating_order:
print('-------------------',machine.state,'-------------------')
machine.next_state()
elif finish:
print('-------------------',machine.state,'-------------------')
(In theory, the code should display the status if a person uses a bot, but later I want to remove this)
full code
A common use case for the application of state machines is to get rid of huge 'if-then-else'-constructs and process events 'context-sensitive', meaning that what happens when an event is received depends on the current state of the machine/model.
While this is probably not of interest for maria_hoffman any longer, google might lead someone here with the same intention:
Let's assume we want to build a simple bot that is capable of adding two numbers. We start with defining the necessary states.
states = ["INIT", "WAITING", "ADD_1", "ADD_2", "QUIT"]
We start from INIT and have a WAITING state where operation instruction are received. We could skip this one but our bot might be extended in the future to also support multiplication. In ADD_1 we expect the first number and in ADD_2 the second number for our sum. When in state QUIT we want the system to shutdown.
Next, we need to define the actual transitions that should happen:
transitions = [
dict(trigger='next', source='WAITING', dest='ADD_1', conditions=lambda x: x == "add"),
dict(trigger='next', source='WAITING', dest='QUIT', conditions=lambda x: x == "quit"),
dict(trigger='next', source='WAITING', dest='WAITING', before="show_error"),
dict(trigger='next', source='ADD_1', dest='ADD_2', before="store_value"),
dict(trigger='next', source='ADD_2', dest='WAITING', before="get_result"),
dict(trigger='reset', source='*', dest='WAITING'),
]
First, we see that we have just two events: next and reset. What happens when next is triggered, depends on the current state. In WAITING we process three possibilities: First, when the parameter passed with event next is equal to add, we transition to ADD_1 and wait for the first number to proces. If the parameter is equal to quit, we transition to QUIT and shutdown the system. If both condition checks fail we will use the third transition which will exit and re-enter WAITING and call a method called show_error before doing so. When transitioning from ADD_1 to ADD_2 we call a function to store the passed value. We need to remember it for get_result which is called when next is received in state ADD_2. Lastly, we have a reset event to roll back things if something did not work out.
Now we are almost done, we just need to define some prompts and the aforementioned methods show_error, store_value and get_result. We create a simple model for this. The idea is to show prompts depending on the state that has been entered. on_enter_<state> is the right tool for this job. We also intialize self.first in __init__ as a field to store the value of the first number that is passed in ADD_1:
class Model:
def __init__(self):
self.first = 0
def on_enter_WAITING(self, *args):
print("Hello, if you want to add two numbers enter 'add'. Enter 'quit' to close the program:", end=' ')
def on_enter_ADD_1(self, *args):
print("Please enter the first value:", end=' ')
def on_enter_QUIT(self, *args):
print("Goodbye!")
def store_value(self, value):
self.first = int(value)
print("Please enter the second value:", end=' ')
def get_result(self, value):
val = int(value)
print(f"{self.first} + {val} = {self.first + val}")
def show_error(self, *args):
print("Sorry, I cannot do that.")
Note that when we want to pass arguments to callbacks, all callbacks need to be able to deal with it. The documentation of transitions states:
There is one important limitation to this approach: every callback function triggered by the state transition must be able to handle all of the arguments. This may cause problems if the callbacks each expect somewhat different data.
So, when we don't need the actual input value, we just put *args in the signature to communicate this.
That's it. Now we tie everything together and implement some rudimentary error checks and we are good to go. We create a model instance and pass it to the machine. When we receive input we pass it to the model via next and let the model do the heavy lifting. While the model is not in state QUIT we will wait for the next input:
model = Model()
machine = Machine(model, states=states, transitions=transitions, initial='INIT')
model.to_WAITING()
while not model.is_QUIT():
inp = input()
try:
model.next(inp)
except ValueError:
print("Oh no! Something went wrong. Let's try again!")
model.reset()
This could be a conversation with the bot:
Hello, if you want to add two numbers enter 'add'. Enter 'quit' to close the program: add
Please enter the first value: 123
Please enter the second value: 4
123 + 4 = 127
Hello, if you want to add two numbers enter 'add'. Enter 'quit' to close the program: call
Sorry, I cannot do that.
Hello, if you want to add two numbers enter 'add'. Enter 'quit' to close the program: add
Please enter the first value: foo
Oh no! Something went wrong. Let's try again!
Hello, if you want to add two numbers enter 'add'. Enter 'quit' to close the program: add
Please enter the first value: 123
Please enter the second value: baz
Oh no! Something went wrong. Let's try again!
Hello, if you want to add two numbers enter 'add'. Enter 'quit' to close the program: quit
Goodbye!
Related
I'm writing a craps game in perl(rules provided via source code comments). One of the features in this craps game written in perl is the ability to ask the user whether they want to continue betting or whether they want to leave the game. The problem I am running into is that any time the user types in 'bet' none of the other functions seem to be getting called and the program simply terminates the same way it should terminate when the user types in 'leave'.
I have tried splitting up the program into even more functions and tracing the direction of the function calls by hand. While I would consider myself to have programing experience in other languages I'm relatively new to perl and the syntax is very different from other languages.
Expected results:
typing bet calls the other functions and depending on the dice roll those functions call other functions and when the user types in leave the program simply terminates.
Actual results:
the program terminates as soon as the user enters anything on the keyboard
use strict;
use warnings;
my $user_money= 500;#AMOUNT OF MONEY THE USER STARTS OUT WITH
my $die=0;#WHAT NUMBER THE DIE ROLLS, IS BETWEEN 1 AND 6
my $total_dicenum=0; #TOTAL VALUE OF THE ROLL
my $betorleave="";#TAKES IN USER INPUT REGARDING WHETHER THEY WANT TO CONTINUE BETTING OR LEAVE THE GAME
my $wager=0;#HOW MUCH MONEY THE USER BETS
my $numrolls=0;#KEEPS TRACK OF THE NUMBER OF TIMES THE USER HAS ROLLED THE DICE
my $player_point=0;#THE PLAYER'S 'POINT' THAT GET'S DETERMINED AFTER THE FIRST ROLL OF DICE
#DETERMINES WHETHER THE PLAYER HAS WON OR LOST A GAME
sub result(){
if($numrolls==1){
if($total_dicenum==7 or $total_dicenum==11){
print"you won!\n";
$player_point=$total_dicenum;
$user_money = $user_money +($wager*2);
$total_dicenum==0;
}
elsif($total_dicenum==2 or $total_dicenum==3 or $total_dicenum==12){
print"you lost\n";
$player_point=$total_dicenum;
$user_money = $user_money-$wager;
$total_dicenum==0;
}
else{
bet();
}
}
else{#ROLLS FOLLWING THE INITAL ROLL
if($total_dicenum==$player_point){
print"you won!\n";
$user_money = $user_money+($wager*2);
$total_dicenum=0;
main();
}
elsif($total_dicenum==7){
print"you lost\n";
$user_money = $user_money-$wager;
$total_dicenum=0;
main();
}
else{
bet();
}
}
}
#DICE ROLLER FUNCTION
sub rollDice(){
print"rolling dice...\n";
$die = 1 + int rand(6);
print"you rolled $die\n";
}
#BETTING FUNCTION
sub bet(){
print"how much money do you want to wager?\n";
print"you currently have $user_money dollars\n";
$wager=<STDIN>;
rollDice();
$total_dicenum+=$die;
print"your total score is $total_dicenum\n";
$numrolls++;
result();
}
#BELOW IS MAIN SUBROUTINE WHERE ALL THE ABOVE SUBROUTINES ARE CALLED
sub main(){
print"Welcome to craps! Here's $user_money dollars!\n";
print"would you like to place a bet or would you like to leave?(bet/leave)\n";
$betorleave=<STDIN>;
if($betorleave eq 'bet'){
bet();
}
if($betorleave eq 'leave'){
print"FINAL BALANCE:\n";
print"$user_money\n";
}
}
#CALLS THE MAIN FUNCTION
main();
#WHAT THIS PROGRAM IS SUPPOSED TO DO:
#Each player will start with $500.00. Initially, and after each turn give the user the option of betting or leaving the program.
#Implement this any way you wish, but make it clear to the user what their options are.
#If the user chooses to place a bet, ask for the amount that will be wagered and start that “turn,” or bet.
#Each turn will consist of one or more rolls of the dice.
#For each roll, the program should display the result of both die and the total value of the roll.
#Then indicate the outcome from that roll (win/lose/continue rolling).
#Once a turn, or bet is finished, indicate the outcome of the bet and the updated balance.
#When the user chooses to exit the program display their final balance.
#Total value of dice after first roll: 7 or 11 – player wins 2, 3, or 12 – player loses
#Any other value and the player rolls again – the total of the dice is now their “point”
#Total value of dice following the initial roll: The players “point” – player wins 7 – player loses
#Any other value and the player rolls again until rolling a 7 or their point
Whenever you read data from STDIN using <STDIN>, you get the newline attached to the end of the string. You usually don't want it, so you remove it using chomp().
So, where you have:
$betorleave=<STDIN>;
You need to have:
chomp($betorleave=<STDIN>);
Some more (free!) tips:
Please learn to indent your code.
Please don't use prototypes on subroutines (unless you can explain why they shouldn't be used in most cases).
Think about the scope of your variables. In general, global variables are bad and inside a subroutine you should only use variables that are either passed into the subroutine as parameters or declared inside the subroutine. Your $betorleave variable, for example, is only ever used in the main() subroutine, so it should be declared inside that subroutine.
I'm trying to retrieve the serial number from a drive using the MC_BR_GetHardwareInfo function block. Since the documentation lacks any kind of example code on this topic I'm getting nowhere.
Which information should I provide to the function block in order to get the desired serial number?
Below sample will crash in the PLC, probably because the function block requires certain pointers to be addressed:
MC_HARDWARE_INFO_REF hwinfo;
MC_BR_GetHardwareInfo(&hwinfo);
You are probably getting a page fault, because you provide the MC_BR_GetHardwareInfo function block (FUB) a wrong type, which leads to random behavior.
A function block is basically a function which requires a reference to a specific type as parameter. This type contains the actual in- and outputs which are used, internal state variables, etc. We need this, because of the synchronous execution of the code. This means unlike a function, you need to call a FUB until it is done.
Let's take a look to the help of the FUB:
Guid: 056444ea-2a15-4af6-a5ae-0675894b17d3
So the FUB needs a reference to the Axis object of which you want to know the HW info and an Execute command. It will give you some status bits, an error code and the actual data you want to have within the structure HardwareInfo of the type MC_HARDWARE_INFO_REF.
First we need to instantiate the FUB by create a variable of its type. We do this in the local *.var file of the task:
VAR
fbGetHwInfo : MC_BR_GetHardwareInfo := (0);
END_VAR
Then we call set the parameters of the FUB and call it, which might look like this:
void _CYCLIC ProgramCyclic(void)
{
//should be set by the application or in watch/monitor; now it only
//executes once
fbGetHwInfo.Execute = 1;
//reference to your axis object; when using a wizard the first axis
//will be gAxis01 on default
fbGetHwInfo.Axis = (UDINT)&gAxis01;
//call the FUB
MC_BR_GetHardwareInfo(&fbGetHwInfo);
if(fbGetHwInfo.Error == 1)
{
//TODO: errorhandling
}
else if(fbGetHwInfo.Done == 1)
{
//TODO use output
//fbGetHwInfo.HardwareInfo
}
}
typically you would do this in some statemachine. Also you probably have to wait until the network to the drive is initialized. You could check this with the MC_BR_ReadDriveStatus FUB. Just for testing it should be enough to wait for some seconds after reboot and set the Execute flag in monitor mode.
There is data validation in my MS Word user form which returns the focus to the textbox where the user entered something incorrect. Now I am trying to accommodate the user's change of mind: instead of correcting the entry, I want him to be able to exit the form (click the Exit command button), in which case the entry would be discarded. I suppose that a solution would start with not using the text box's exit event. I little help from someone who knows the answer would save me a lot of testing time, perhaps to find out that I can't do it.
Does anyone know?
I understand that you are handling the Exit event of the Textbox, setting the Cancel output parameter if the data is not valid.
There's a tricky but simple solution that permits to keep that working and still have an Exit button. It permits to activate the handler of the Exit button without requiring the focus to leave the Textbox. This way you can unload the Form safely in this handler.
Try this it works pretty smoothly:
1- Set the property TakeFocusOnClick of the Exit command button to False. You can do that at design time in the property-sheet, or at run-time i.e. at UserForm_Activate
2- just unload the form when the Exit button is clicked:
Private Sub ExitButton_Click()
Unload Me
End Sub
#A.S.H provided the key to the solution below. His point is that it is possible to call another event procedure while Cancel is active in the Exit procedure of a control. That other procedure can be used to rectify the condition in the first control which is triggering the Cancel, thereby enabling an orderly exit. The all-enabling condition is that the control on whose click event the rectifying procedure is to run must not take the focus when clicked (meaning it can run without triggering an exit from the control stuck on Cancel). I have added code to the exit procedure to set CmdExit.TakeFocusOnClick = False when a Cancel condition arises there. Now, ...
Private Sub CmdExit_Click()
' 12 May 2017
' if CmdExit can't take the focus it can't be the ActiveControl
If Not ActiveControl Is CmdExit Then
Select Case ActiveControl.Name
Case "Cbx107"
Cbx107.Value = ""
Case "Tbx53"
Tbx53.Value = "0"
End Select
With CmdExit
If Not .TakeFocusOnClick Then
.TakeFocusOnClick = True
.SetFocus
End If
End With
End If
' now CmdExit is the ActiveControl
MsgMe "Cmd Exit: ActiveControl = " & ActiveControl.Name
Me.Hide
End Sub
I want to know when a switch removes any flow rule after hard_timeout is passed. I know ofp_flow_mod has an attribute flags where I can test OFPFF_SEND_FLOW_REM. I made the following in my controller:
def handle_flowRemoval(self, event):
msg = event.parsed
if msg.flags == of.OFPFF_SEND_FLOW_REM:
print ("The switch %s has raised the removal event!" % event.dpid)
In fact it does not trigger this method after hard_timeout is expired. I don't know why. Can anyone suggest how to fix it.
Tank you
If you are sure there are flows installed on the switch best approach would be to add the topology module and listen to the FlowRemoved event mixin
In your main class add something like
core.openflow.addListenerByName("FlowRemoved", self._handle_flow_removal)
and then somewhere to read the event
def _handle_flow_removal (self, event):
"""
handler flow removed event here
"""
print event.__dict__() # to get available info
The FlowRemoved event mixin is in the POX topology.py module at line 172
https://github.com/noxrepo/pox/blob/carp/pox/openflow/topology.py
I am running into a problem with a simple program that changes the color of the background when it receives a command from a different machine through TCP. It takes like thirty seconds to change the color. I am running this through the local network so it should be near instant response. I am using wxPython for the frame. I don't think I have overly complicated code. Relevant code:
threader=threading.Thread(target=self.threading)
threader.start()
def threading(self):
host="192.168.1.122"
port=4100
s=socket.socket()
s.bind((host,port))
s.listen(1)
c,addr=s.accept()
print "Connected"
while 1:
data=c.recv(1024)
if not data:
break
data=data.split("_")
reading=int(data[1])
pin=int(data[0])
if pin == 1:
if reading<20:
self.front_left.SetBackgroundColour("red")
elif pin == 2:
if reading<20:
self.front_right.SetBackgroundColour("red")
elif pin == 3:
if reading<20:
self.bottom_left.SetBackgroundColour("red")
elif pin == 4:
if reading<20:
self.bottom_right.SetBackgroundColour("red")
else:
pass
c.close()
I need this code to be instant as this will be going on a robot that will tell if objects are too close(which is why there is red background when it gets within 20 cm of object). Any help will be greatly appreciated!
It appears that you are attempting to update wxPython code from a thread. This action is unsupported / undefined in wxPython. You need to use thread-safe methods to update the wxPython UI, such as wx.CallAfter or wx.PostEvent. See the following wxPython wiki page for some examples:
http://wiki.wxpython.org/LongRunningTasks
Basically, you'll want to do something like this in your if statements:
wx.CallAfter(self.bottom_right.SetBackgroundColour, "red")