Modules¶
Message¶
In order to effectively communicate between different parts of the application, all of the components must know what to expect when they get information. To enable this, messages should be created with the Message class.
This provides a consistent layout for handling messsages throughout the application. It is set up to be minimal and extendable. There are also various helper functions to automatically fill in some of these fields in common scenarios.
sender and receiver are a list of strings, which becomes useful when complex message routing is required.
command is simply required to ensure there is a straightforward way to identify different message types when handling messages.
package actually carries the data and may be of any type. From experience, dictionaries work well. Adding labels to the data does not add much overhead, but does make the message much easier to parse, allowing for changes in input order, new fields etc.
-
class
pysimpleapp.message.Message(sender: List[str], receiver: List[str], command: str, package: any)[source]¶ Generic message class which will be used to move information around the system
-
__init__(sender: List[str], receiver: List[str], command: str, package: any)[source]¶ Create the message with data about where the message came from, where it is going, what it should do and any supplementary information. The package can be of any type, dictionaries tend to be useful for decoding on the other end.
Parameters: - sender – Address of the object which sent the message
- receiver – Address of the intended recipient of the message
- command – Headline of the message to allow simple handling
- package – Data transferred as part of the message
-
Threads¶
-
class
pysimpleapp.threads.simple_threads.SimpleThread(name: str, owner: List[str], input_queue: queue.Queue, output_queue: queue.Queue)[source]¶ *SimpleThread* is an abstract class which will provide the basis for building out the threads provided in pysimpleapp.
Methods which must be specified by children:
- create_params - creates the parameters which should be used during main execution
- main - the body of the thread which performs the functionality
- _control_loop - describes how the class executes the main function
It executes its main function as part of a control loop which is specified by child classes. Before it starts, the parameters can be updated and it can even be ended and prevented from ever running.
Must be initialised with a name, owner, input and output queues.
The operation of the thread is implemented in main() This function should be overridden during implementation.
Parameters are created in create_params() which should be overridden when implemented. Parameters should not be created from anywhere else. Updates to the parameter will be collected after each run of the main function.
Messages are handled based on a lookup table called the address_book. There are some in-built commands which can be easily accessed, but the addresses can be changed easily. To add custom commands to the thread, simply create a function in the class which takes a pysimpleapp.message.Message object and give it a key in the address book.
Example
self.address_book["my_custom_command"] = self.my_custom_command_handler # During message handling, this will be called as: self.address_book["my_custom_command"](messsage)
-
__init__(name: str, owner: List[str], input_queue: queue.Queue, output_queue: queue.Queue)[source]¶ Create the thread, set up control flags and call _create_params
Parameters: - name – Identifier for the thread, could be list of strings depending on communication model
- owner – Address of object which asked for this thread to be created
- input_queue – Input pipe for messages which the thread is expected to process
- output_queue – Output pipe for messages which should be sent elsewhere
-
classmethod
create_params()[source]¶ Override this function to add parameters. This should be the only method used to create parameters in a thread. Example:
self.parameters["param 1"] = 0 self.parameters["param 2"] = "some string"
-
custom_handler(message: pysimpleapp.message.Message)[source]¶ Override this function to handle custom user messages. Expect a pysimpleapp.Message object as an input, no return value is expected or handled by default
Good idea: Include a command as a key in a package dictionary to reliably handle custom messages. This will be much clearer than sorting by type, size or looking for a specific key or list value.
-
classmethod
main()[source]¶ Override this function to provide the thread with instructions.
main provides the functionality for the thread. It may contain heavy computations, IO processing and anything you want to do without having to worry about exactly how long it takes.
Good ideas:
- Read from parameters to decide on program execution
- Send pysimpleapp.Message objects out through the output_queue with feedback and information
- Handle exceptions to prevent unnecessary failures of the thread
Bad ideas:
- Writing to parameters during program execution
- Writing things back to the input_queue
- Implementing operations which take a long time to complete and expecting fast reactions in changes to parameters or THREAD_STOP commands
-
run()[source]¶ This defines the programatic flow for the thread.
Threads wait for messages on the input_queue and then process them.
If an end command is given, the thread will return True and exit, therefore no longer being able to run.
If a start command is given and no end command is requested, the _control_loop function will be called. While running, the queue will not be processed.
If the control loop raises an exception, the thread will exit gracefully.
Single Run Thread¶
-
class
pysimpleapp.threads.simple_threads.SingleRunThread(name: str, owner: List[str], input_queue: queue.Queue, output_queue: queue.Queue)[source]¶ *SingleRunThread* is a child of *SimpleThread* which runs the main function once and then ends.
It will execute after a start command and then exit.
main and create_params are left as abstract methods for the user to implement.
Multi Run Thread¶
-
class
pysimpleapp.threads.simple_threads.MultiRunThread(name: str, owner: List[str], input_queue: queue.Queue, output_queue: queue.Queue)[source]¶ *MultiRunThread* is a child of *SimpleThread* which may run the main function repeatedly.
It will execute after a start command, and then await further instruction. This may be another start command, in which case the main function will repeat. Or it may be an update or end command, which will cause the parameters to update or the thread to end, respectively.
Other than that, it works exactly the same way as the *SimpleThread*.
main and _create_params are left as abstract methods for the user to implement.
Repeating Thread¶
-
class
pysimpleapp.threads.simple_threads.RepeatingThread(name: str, owner: List[str], input_queue: queue.Queue, output_queue: queue.Queue)[source]¶ *RepeatingThread* is a child of *SimpleThread* which runs its main function on a regular basis.
It differs from *MultiRunThread* in that once it has been started, it will trigger its own runs. It may be stopped with the “THREAD_STOP” command. Having received such a command, the thread will be waiting to start again and will not end without a THREAD_END command.
RepeatingThread has a parameter of “loop_timer” which specifies the time, in seconds, after program execution to wait before running main again. Default is 1s.
This is achieved by adding a start message to its own input queue after “loop_timer” amount of time. Therefore, it is susceptible to being flooded by other messages. It may also be triggered by sending another start command.
So, to use safely, set parameters before starting and do not flood with parameters while running. Issue a THREAD_STOP command before performing large changes to the parameters.
main and create_params are left as abstract methods for the user to implement.
Precise Repeating Thread¶
-
class
pysimpleapp.threads.simple_threads.PreciseRepeatingThread(name, owner, input_queue: queue.Queue, output_queue: queue.Queue)[source]¶ Like the repeating thread but with more of an emphasis on getting the timing exactly right.
Use command “SET_LOOP_TIMER” with a package of {“loop_timer”: $time} to ensure changes are handled properly.
Subtracts the amount of time taken for the main function exection from the time to wait until the next call. Use this if there is a function for which regularity is important but functionality can occasionally be slow.
If main execution takes longer than loop_timer, an error message will be logged.