# Algorithm of the Python Framework

The Python framework script, launched via the Windows Scheduler, must follow the following algorithm:

1. Perform an HTTP POST request to the `/api/robot/heartbeat` script of the Orchestrator. Pass the following parameters:
   1. The GUID of this Robot (RobotGUID),
   2. The name of the computer (Machine),
   3. The IP address of the machine (IP),
   4. The MAC address of the machine (MAC),
   5. The current status of the Robot (Status),
   6. The GUID of the currently executing Job (JobGUID),
   7. The name of the currently executing Job (JobName),
   8. The type of Job (JobType, must always be of type Orchestrated, value 1).

Output data (in JSON format):

* HeartbeatErrorCode,
* ErrorText,
* HasNewJob.

The script may return other parameters in the response that are not used in the context of the current Task.

Robot status reference:

```
Status_Disconnected = 0; # robot is disconnected
Status_Unlicensed     = 1; # robot is unlicensed
Status_Ready        = 2; # robot is not performing jobs and is ready to accept new jobs
Status_Working    = 3; # robot is performing the current job
```

ErrorCode values reference:

```
HeartbeatErrorCode_NoError = 0;  # no errors
HeartbeatErrorCode_RobotNotFound = 101;  # robot with the specified GUID not found
HeartbeatErrorCode_JobAborting = 10;   # the orchestrator requested a hard stop of this script (Abort)
HeartbeatErrorCode_JobStopping = 20;   # the orchestrator requested a soft stop of this script (Stop)
```

HasNewJob values reference:

```
HasNewJob_NoJob = 0 # there are no new jobs for this robot
HasNewJob_JobFound = 1 # there are new jobs for this robot that need to be retrieved using the ConsumeNextJob call
```

When the script is first run, it is necessary to pass the value `Status = 2` (Status\_Ready). Also, during the first run, when the name and GUID of the executing job are still unknown, these fields may remain empty.

Example call:

```
import requests
import base64
import json

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
host = 'http://192.168.1.111:4500' #orchestrator host
robot_guid = 'da8bc2f0-8065-4385-b867-405e66b8e151' #robot GUID
headers = {
    'Authorization': 'Basic ' + base64.b64encode(robot_guid.encode('utf-8')).decode('utf-8')
}
req = {
    'RobotGUID': robot_guid,   #robot GUID
    'Machine': robot_machine,   #Machine name
    'IP': robot_ip,   #Robot IP address
    'MAC': robot_mac,   #Robot MAC address
    'Status': 2,   #robot status
    'JobGUID': '',  #Job GUID
    'JobName': '',   #name of the executing script, i.e., python_script_name hereafter
    'JobType': 1   #job type, always value 1
}
r = requests.post(host + '/api/robot/heartbeat', data=req, headers=headers, verify=False)
heartbeat_results = json.loads(r.text)
print(heartbeat_results['HeartbeatErrorCode'])
print(heartbeat_results['ErrorText'])
print(heartbeat_results['HasNewJob'])
```

2. If the response is `HasNewJob == 0`, terminate the execution of the framework script and log off from the Windows account using Log off.\
   \
   If the response is `HasNewJob == 1`, perform an HTTP POST request to the `/api/robot/consumeNextJob` script of the Orchestrator.\
   \
   Input data:

   1. Robot GUID (RobotGUID),
   2. Current status of the Robot (Status).

   \
   Output data (in JSON format):

   1. ConsumeJobErrorCode,
   2. ErrorText,
   3. Job,
   4. ProcessVersion,
   5. Task.

   ConsumeJobErrorCode values reference:\
   \
   `ConsumeJobErrorCode = 0; # no errors`\
   `ConsumeJobErrorCode = 101; # robot with the specified GUID not found`\
   `ConsumeJobErrorCode = 102; # new job not found`\
   \
   Example call:\
   \
   `import requests`\
   `import base64`\
   `import json`\
   \
   `from requests.packages.urllib3.exceptions import InsecureRequestWarning`\
   `requests.packages.urllib3.disable_warnings(InsecureRequestWarning)`\
   \
   `host = 'http://192.168.1.111:4500' #orchestrator host`\
   `robot_guid = 'da8bc2f0-8065-4385-b867-405e66b8e151' #robot GUID`\
   \
   `headers = {`\
   `'Authorization': 'Basic ' + base64.b64encode(robot_guid.encode('utf-8')).decode('utf-8')`\
   `}`\
   \
   `cnj = {`\
   `'RobotGUID': robot_guid #robot GUID`\
   `}`\
   \
   `r = requests.post(host + '/api/robot/consumeNextJob', data=cnj, headers=headers, verify=False)`\
   `consume_result = json.loads(r.text)`\
   `print(consume_result['ConsumeJobErrorCode'])`\
   `print(consume_result['ErrorText'])`\
   `new_job = json.loads(consume_result['Job'])`\
   `job_guid = new_job['GUID']`\
   `process_version = json.loads(consume_result['ProcessVersion'])`\
   `python_script_name = process_version['Name'] #Assuming that the name of the robot script to be executed is passed in the process version name`\
   `task = json.loads(consume_result[' Task '])`<br>
3. In case of successfully receiving a new Job from the Orchestrator, perform an HTTP PUT request to the `/api/job/update` script of the Orchestrator. Pass the GUID of the received Job (guid) and the new Job status `Status_In_Progress`.\
   \
   Job status values reference:\
   \
   `Status_Created = 0; #Job created`\
   `Status_Pending = 1; #Job is waiting for the robot to take it on`\
   `Status_Aborting = 2; #Job is in the process of hard stopping (interrupting)`\
   `Status_Aborted = 3; #Job has been successfully interrupted`\
   `Status_Success = 4; #Job has been successfully completed`\
   `Status_Failed = 5; #Job has not been successfully completed`\
   `Status_Stopping = 6; #Job is in the process of soft stopping`\
   `Status_Stopped = 7; #Job has been successfully stopped after a soft stop request`\
   `Status_In_Progress = 8; #Job is currently being executed`\
   \
   Example call:\
   \
   `import requests`\
   `import base64`\
   `import json`\
   \
   `from requests.packages.urllib3.exceptions import InsecureRequestWarning`\
   `requests.packages.urllib3.disable_warnings(InsecureRequestWarning)`\
   `host = 'http://192.168.1.111:4500' #orchestrator host`\
   `robot_guid = 'da8bc2f0-8065-4385-b867-405e66b8e151' #robot GUID`\
   \
   `headers = {`\
   `'Authorization': 'Basic ' + base64.b64encode(robot_guid.encode('utf-8')).decode('utf-8')`\
   `}`\
   \
   `job = {`\
   `'guid': job_guid, #GUID of the current job`\
   `'status': 8 #new job status`\
   `}`\
   \
   `r = requests.put(host + '/api/job/update', data=job, headers=headers, verify=False)`\
   `print(r.text)`<br>
4. Start executing the Python script of the Robot with the name equal to `python_script_name`. If possible, periodically repeat requests to the `/api/robot/heartbeat` script with the correct values for `JobGUID` and `Status = 3 (Status_Working)`.\
   \
   It is recommended to send this request no more than once every 10 seconds. If the response status is `HeartbeatErrorCode == 10`, immediately stop (abort) the execution of the Python script of the Robot.\
   \
   If the response status is `HeartbeatErrorCode == 20`, perform a proper termination of the Python script of the Robot.
5. After the completion of the Python script of the Robot, perform an HTTP PUT request to the `/api/job/update` script of the Orchestrator. Pass the GUID of the current Job of the Robot (guid) and the new Job status depending on the result of its completion according to the reference provided above.
6. Then perform an HTTP POST request to the `/api/robot/heartbeat` script of the Orchestrator with the status `Status = 2 (Status_Ready)` and return to step 2 of the framework algorithm.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sherparpa.ru/en/sherpa-rpa/sherpa-orchestrator/python-sherpa-framework/algoritm-raboty-python-freimvorka.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
