-
Notifications
You must be signed in to change notification settings - Fork 16
Activity Types and SDK
All activities in RPAForge are classified into the following types:
| Type | Description | Multiple Inputs | Multiple Outputs | Timeout | Nested |
|---|---|---|---|---|---|
| Control | Start/End points | Configurable | Configurable | No | No |
| Loop | Iteration constructs | 1 | 1 | Yes | Yes |
| Condition | Branching logic | 1 | 2+ | No | Yes |
| Container | Grouping activities | 1 | 1 | Yes | Yes |
| Synchronous | Blocking operations | 1 | 1 | Yes | No |
| Asynchronous | Non-blocking operations | 1 | 1 | No | No |
| Error Handler | Exception handling | 1 | 1 | No | Yes |
| Sub-Diagram | Reusable processes | 1 | 1 | Yes | No |
┌─────────────────────────────────────────────────────────────────┐
│ Base Activity │
├─────────────────────────────────────────────────────────────────┤
│ id: string │
│ type: ActivityType │
│ name: string │
│ category: ActivityCategory │
│ description: string │
│ icon: string │
│ color: string │
├─────────────────────────────────────────────────────────────────┤
│ ports: PortConfig │
│ ├── inputs: Port[] │
│ └── outputs: Port[] │
├─────────────────────────────────────────────────────────────────┤
│ properties: PropertySchema[] │
│ timeout?: TimeoutConfig │
│ retry?: RetryConfig │
│ onError?: ErrorHandlingConfig │
└─────────────────────────────────────────────────────────────────┘
Control activities define process entry and exit points.
Start Activity
interface StartActivity {
type: 'control-start';
ports: {
inputs: []; // No inputs
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
processName: string;
description?: string;
tags?: string[];
};
}End Activity
interface EndActivity {
type: 'control-end';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: []; // No outputs
};
properties: {
status: 'pass' | 'fail';
message?: string;
};
}Loop activities iterate over collections or repeat until condition is met.
While Loop
interface WhileActivity {
type: 'loop-while';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
condition: string;
maxIterations?: number;
timeout?: TimeoutConfig;
};
nested: true; // Can contain activities
}For Each Loop
interface ForEachActivity {
type: 'loop-foreach';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
itemVariable: string;
collection: string;
parallel?: boolean;
timeout?: TimeoutConfig;
};
nested: true;
}Condition activities provide branching logic based on expressions.
If Condition
interface IfActivity {
type: 'condition-if';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [
{ id: 'true', type: 'flow', label: 'True' },
{ id: 'false', type: 'flow', label: 'False' }
];
};
properties: {
condition: string;
};
nested: true;
branches: ['true', 'false'];
}Switch Condition
interface SwitchActivity {
type: 'condition-switch';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [
{ id: 'case1', type: 'flow', label: 'Case 1' },
{ id: 'case2', type: 'flow', label: 'Case 2' },
{ id: 'default', type: 'flow', label: 'Default' }
];
};
properties: {
value: string;
cases: CaseDefinition[];
};
nested: true;
dynamicOutputs: true; // Outputs can be added/removed
}Container activities group related activities with shared context.
Scope Container
interface ScopeActivity {
type: 'container-scope';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
name: string;
timeout?: TimeoutConfig;
variables?: VariableDefinition[];
};
nested: true;
}Application Container
interface ApplicationActivity {
type: 'container-application';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
application: 'excel' | 'word' | 'browser' | 'outlook' | 'custom';
connectionConfig: ConnectionConfig;
timeout?: TimeoutConfig;
autoClose: boolean;
};
nested: true;
}Parallel Container
interface ParallelActivity {
type: 'container-parallel';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
branches: number; // Number of parallel branches
synchronize: boolean; // Wait for all to complete
timeout?: TimeoutConfig;
};
nested: true;
parallelBranches: true;
}Synchronous activities block execution until completion.
Characteristics:
- Single input and output
- Configurable timeout
- Retry support
- Error handling options
interface SyncActivity {
type: 'sync';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
action: string;
arguments: ArgumentDefinition[];
timeout: TimeoutConfig;
retry?: RetryConfig;
continueOnError: boolean;
};
nested: false;
}Timeout Configuration:
interface TimeoutConfig {
enabled: boolean;
duration: number; // milliseconds
onTimeout: 'error' | 'skip' | 'retry';
}Retry Configuration:
interface RetryConfig {
enabled: boolean;
maxAttempts: number;
interval: number; // milliseconds
backoff: 'none' | 'linear' | 'exponential';
retryOn: string[]; // Error types to retry on
}Asynchronous activities don't block execution.
Characteristics:
- Single input and output
- No timeout (fire and forget)
- Optional callback support
interface AsyncActivity {
type: 'async';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [
{ id: 'output', type: 'flow' },
{ id: 'callback', type: 'event', optional: true }
];
};
properties: {
action: string;
arguments: ArgumentDefinition[];
waitForCompletion: boolean;
callbackTimeout?: number;
};
nested: false;
}Error handler activities manage exceptions.
Try-Catch
interface TryCatchActivity {
type: 'error-trycatch';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
tryBlock: Activity[];
catchBlocks: CatchDefinition[];
finallyBlock?: Activity[];
};
nested: true;
}
interface CatchDefinition {
id: string;
exceptionType: string; // e.g., 'ValueError', 'TimeoutError'
variable?: string; // Variable to store exception
handler: Activity[];
}Throw
interface ThrowActivity {
type: 'error-throw';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: []; // No outputs - execution stops
};
properties: {
exceptionType: string;
message: string;
};
nested: false;
}Sub-diagram activities call reusable processes.
interface SubDiagramActivity {
type: 'subdiagram';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
diagramId: string;
diagramName: string;
parameters: ParameterMapping[];
returns: ReturnMapping[];
timeout?: TimeoutConfig;
};
nested: false;
}type PortType =
| 'flow' // Control flow (execution)
| 'data' // Data flow (values)
| 'event' // Event output (async)
| 'error'; // Error output
interface Port {
id: string;
type: PortType;
label?: string;
required: boolean;
multiple?: boolean; // Can accept multiple connections
dataType?: string; // For data ports
position: 'left' | 'right' | 'top' | 'bottom';
}interface PortConfig {
inputs: Port[];
outputs: Port[];
dynamic?: {
inputs?: boolean; // Can add inputs at runtime
outputs?: boolean; // Can add outputs at runtime
};
}from rpaforge.sdk import activity, ActivityType, Port, Property
@activity(
type=ActivityType.SYNC,
name="Click Element",
category="Web Automation",
icon="🖱️",
description="Click on a web element",
timeout_default=30000,
)
class ClickElement:
"""Click on a web element with optional retry."""
# Define ports
input_ports = [
Port(id="input", type="flow", required=True)
]
output_ports = [
Port(id="output", type="flow", required=True),
Port(id="not_found", type="flow", required=False, label="Not Found")
]
# Define properties
properties = [
Property(
name="selector",
type="string",
required=True,
description="CSS selector or XPath",
),
Property(
name="wait_time",
type="number",
default=5000,
description="Time to wait for element",
),
Property(
name="retry_count",
type="number",
default=3,
description="Number of retry attempts",
),
]
def execute(self, context):
"""Execute the activity."""
selector = context.get_property("selector")
wait_time = context.get_property("wait_time")
retry_count = context.get_property("retry_count")
for attempt in range(retry_count):
try:
element = context.wait_for_element(selector, wait_time)
element.click()
return context.output("output")
except ElementNotFoundError:
if attempt == retry_count - 1:
return context.output("not_found")
continue
return context.output("output")from rpaforge.sdk import ContainerActivity, ActivityType, Port, Property
@activity(
type=ActivityType.CONTAINER,
name="Excel Application",
category="Data Operations",
icon="📊",
description="Work with Excel application",
)
class ExcelApplication(ContainerActivity):
"""Container for Excel operations."""
input_ports = [Port(id="input", type="flow")]
output_ports = [Port(id="output", type="flow")]
properties = [
Property(
name="workbook_path",
type="string",
description="Path to Excel workbook",
),
Property(
name="visible",
type="boolean",
default=True,
description="Show Excel window",
),
Property(
name="auto_close",
type="boolean",
default=True,
description="Close Excel when done",
),
]
def enter(self, context):
"""Called when entering the container."""
workbook_path = context.get_property("workbook_path")
visible = context.get_property("visible")
excel = context.create_excel_instance(visible=visible)
if workbook_path:
excel.open(workbook_path)
context.set_state("excel_instance", excel)
return True
def exit(self, context):
"""Called when exiting the container."""
excel = context.get_state("excel_instance")
if context.get_property("auto_close") and excel:
excel.close()
return Truefrom rpaforge.sdk import LoopActivity, ActivityType, Port, Property
@activity(
type=ActivityType.LOOP,
name="For Each Row",
category="Data Operations",
icon="🔄",
description="Iterate over table rows",
)
class ForEachRow(LoopActivity):
"""Iterate over each row in a data table."""
input_ports = [Port(id="input", type="flow")]
output_ports = [Port(id="output", type="flow")]
properties = [
Property(
name="data_source",
type="variable",
required=True,
description="Variable containing data table",
),
Property(
name="row_variable",
type="string",
default="${row}",
description="Variable for current row",
),
Property(
name="index_variable",
type="string",
default="${index}",
description="Variable for row index",
),
]
def get_iterations(self, context):
"""Return number of iterations."""
data = context.get_variable(context.get_property("data_source"))
return len(data) if data else 0
def before_iteration(self, context, index):
"""Called before each iteration."""
data = context.get_variable(context.get_property("data_source"))
row_variable = context.get_property("row_variable")
index_variable = context.get_property("index_variable")
context.set_variable(row_variable, data[index])
context.set_variable(index_variable, index)
return True
def after_iteration(self, context, index):
"""Called after each iteration."""
return Truefrom rpaforge.sdk import ConditionActivity, ActivityType, Port, Property
@activity(
type=ActivityType.CONDITION,
name="Switch",
category="Flow Control",
icon="⇄",
description="Multi-way branch based on value",
)
class SwitchActivity(ConditionActivity):
"""Switch statement for multiple branches."""
input_ports = [Port(id="input", type="flow")]
output_ports = [
Port(id="default", type="flow", label="Default")
] # Dynamic outputs added at runtime
properties = [
Property(
name="value",
type="expression",
required=True,
description="Value to switch on",
),
Property(
name="cases",
type="array",
default=[],
description="Case definitions",
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"value": {"type": "string"},
"label": {"type": "string"},
}
}
}
),
]
def get_branches(self, context):
"""Return available output branches."""
cases = context.get_property("cases")
branches = [{"id": f"case_{i}", "label": case["label"]}
for i, case in enumerate(cases)]
branches.append({"id": "default", "label": "Default"})
return branches
def evaluate(self, context):
"""Evaluate and return branch to take."""
value = context.evaluate(context.get_property("value"))
cases = context.get_property("cases")
for i, case in enumerate(cases):
if str(value) == str(case["value"]):
return f"case_{i}"
return "default"from rpaforge.sdk import ErrorHandlerActivity, ActivityType, Port, Property
@activity(
type=ActivityType.ERROR_HANDLER,
name="Try Catch",
category="Error Handling",
icon="⚠️",
description="Handle exceptions gracefully",
)
class TryCatchActivity(ErrorHandlerActivity):
"""Try-catch-finally block."""
input_ports = [Port(id="input", type="flow")]
output_ports = [Port(id="output", type="flow")]
properties = [
Property(
name="catch_blocks",
type="array",
default=[],
description="Exception handlers",
schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"exception_type": {"type": "string"},
"variable": {"type": "string"},
}
}
}
),
]
def get_try_block(self, context):
"""Return activities for try block."""
return context.get_nested_activities("try")
def get_catch_blocks(self, context):
"""Return exception handlers."""
return context.get_property("catch_blocks")
def get_finally_block(self, context):
"""Return activities for finally block."""
return context.get_nested_activities("finally")
def on_exception(self, context, exception):
"""Handle exception and return matching catch block."""
catch_blocks = context.get_property("catch_blocks")
for i, catch in enumerate(catch_blocks):
if self.matches_exception(exception, catch["exception_type"]):
if catch.get("variable"):
context.set_variable(catch["variable"], str(exception))
return f"catch_{i}"
# Re-raise if no matching catch
raise exceptionfrom rpaforge.sdk import AsyncActivity, ActivityType, Port, Property
import asyncio
@activity(
type=ActivityType.ASYNC,
name="Run Process",
category="System",
icon="▶️",
description="Run external process asynchronously",
)
class RunProcess(AsyncActivity):
"""Run an external process."""
input_ports = [Port(id="input", type="flow")]
output_ports = [
Port(id="output", type="flow"),
Port(id="callback", type="event", optional=True)
]
properties = [
Property(
name="command",
type="string",
required=True,
description="Command to execute",
),
Property(
name="arguments",
type="array",
default=[],
description="Command arguments",
),
Property(
name="wait_for_completion",
type="boolean",
default=False,
description="Wait for process to complete",
),
]
async def execute_async(self, context):
"""Execute asynchronously."""
command = context.get_property("command")
args = context.get_property("arguments")
wait = context.get_property("wait_for_completion")
process = await asyncio.create_subprocess_exec(
command, *args,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
if wait:
stdout, stderr = await process.communicate()
context.set_variable("${stdout}", stdout.decode())
context.set_variable("${stderr}", stderr.decode())
context.set_variable("${exit_code}", process.returncode)
else:
context.set_variable("${pid}", process.pid)
return context.output("output")from rpaforge.sdk import ActivityRegistry
# Register activities
registry = ActivityRegistry()
registry.register(ClickElement)
registry.register(ExcelApplication)
registry.register(ForEachRow)
registry.register(SwitchActivity)
registry.register(TryCatchActivity)
registry.register(RunProcess)
# Get activity metadata
metadata = registry.get_metadata("ClickElement")
print(metadata.to_dict())
# List all activities by category
for category in registry.get_categories():
activities = registry.get_by_category(category)
print(f"{category}: {[a.name for a in activities]}")interface ActivityMetadata {
id: string;
type: ActivityType;
name: string;
category: string;
icon: string;
description: string;
version: string;
ports: {
inputs: PortDefinition[];
outputs: PortDefinition[];
dynamic?: DynamicPortConfig;
};
properties: PropertyDefinition[];
execution: {
timeout: TimeoutConfig;
retry: RetryConfig;
continueOnError: boolean;
};
validation: ValidationRule[];
robotFramework: {
keyword: string;
library: string;
arguments: string[];
documentation: string;
};
}Все активности в RPAForge классифицируются по следующим типам:
| Тип | Описание | Множественные входы | Множественные выходы | Таймаут | Вложенность |
|---|---|---|---|---|---|
| Управление | Точки старта/останова | Настраивается | Настраивается | Нет | Нет |
| Цикл | Конструкции итерации | 1 | 1 | Да | Да |
| Условие | Логика ветвления | 1 | 2+ | Нет | Да |
| Контейнер | Группировка активностей | 1 | 1 | Да | Да |
| Синхронный | Блокирующие операции | 1 | 1 | Да | Нет |
| Асинхронный | Неблокирующие операции | 1 | 1 | Нет | Нет |
| Обработчик ошибок | Обработка исключений | 1 | 1 | Нет | Да |
| Поддиаграмма | Повторно используемые процессы | 1 | 1 | Да | Нет |
┌─────────────────────────────────────────────────────────────────┐
│ Базовая активность │
├─────────────────────────────────────────────────────────────────┤
│ id: string // Идентификатор │
│ type: ActivityType // Тип активности │
│ name: string // Название │
│ category: ActivityCategory // Категория │
│ description: string // Описание │
│ icon: string // Иконка │
│ color: string // Цвет │
├─────────────────────────────────────────────────────────────────┤
│ ports: PortConfig // Конфигурация портов │
│ ├── inputs: Port[] // Входные порты │
│ └── outputs: Port[] // Выходные порты │
├─────────────────────────────────────────────────────────────────┤
│ properties: PropertySchema[] // Свойства │
│ timeout?: TimeoutConfig // Таймаут │
│ retry?: RetryConfig // Повторные попытки │
│ onError?: ErrorHandlingConfig // Обработка ошибок │
└─────────────────────────────────────────────────────────────────┘
Определяют точки входа и выхода процесса.
Старт
interface StartActivity {
type: 'control-start';
ports: {
inputs: []; // Нет входов
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
processName: string;
description?: string;
tags?: string[];
};
}Стоп
interface EndActivity {
type: 'control-end';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: []; // Нет выходов
};
properties: {
status: 'pass' | 'fail';
message?: string;
};
}Выполняют итерацию по коллекциям или повторяют до выполнения условия.
Цикл While
interface WhileActivity {
type: 'loop-while';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
condition: string; // Условие продолжения
maxIterations?: number; // Максимум итераций
timeout?: TimeoutConfig; // Таймаут выполнения
};
nested: true; // Может содержать активности
}Цикл For Each
interface ForEachActivity {
type: 'loop-foreach';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
itemVariable: string; // Переменная элемента
collection: string; // Коллекция для итерации
parallel?: boolean; // Параллельное выполнение
timeout?: TimeoutConfig;
};
nested: true;
}Обеспечивают логику ветвления на основе выражений.
Условие If
interface IfActivity {
type: 'condition-if';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [
{ id: 'true', type: 'flow', label: 'Истина' },
{ id: 'false', type: 'flow', label: 'Ложь' }
];
};
properties: {
condition: string; // Условное выражение
};
nested: true;
branches: ['true', 'false'];
}Переключатель Switch
interface SwitchActivity {
type: 'condition-switch';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [
{ id: 'case1', type: 'flow', label: 'Вариант 1' },
{ id: 'case2', type: 'flow', label: 'Вариант 2' },
{ id: 'default', type: 'flow', label: 'По умолчанию' }
];
};
properties: {
value: string; // Значение для сравнения
cases: CaseDefinition[]; // Определения вариантов
};
nested: true;
dynamicOutputs: true; // Выходы можно добавлять/удалять
}Группируют связанные активности с общим контекстом.
Контейнер области видимости
interface ScopeActivity {
type: 'container-scope';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
name: string;
timeout?: TimeoutConfig;
variables?: VariableDefinition[];
};
nested: true;
}Контейнер приложения
interface ApplicationActivity {
type: 'container-application';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
application: 'excel' | 'word' | 'browser' | 'outlook' | 'custom';
connectionConfig: ConnectionConfig;
timeout?: TimeoutConfig;
autoClose: boolean; // Автозакрытие при завершении
};
nested: true;
}Параллельный контейнер
interface ParallelActivity {
type: 'container-parallel';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
branches: number; // Количество параллельных веток
synchronize: boolean; // Ждать завершения всех
timeout?: TimeoutConfig;
};
nested: true;
parallelBranches: true;
}Блокируют выполнение до завершения.
Характеристики:
- Один вход и выход
- Настраиваемый таймаут
- Поддержка повторных попыток
- Опции обработки ошибок
interface SyncActivity {
type: 'sync';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
action: string;
arguments: ArgumentDefinition[];
timeout: TimeoutConfig;
retry?: RetryConfig;
continueOnError: boolean;
};
nested: false;
}Конфигурация таймаута:
interface TimeoutConfig {
enabled: boolean;
duration: number; // миллисекунды
onTimeout: 'error' | 'skip' | 'retry';
}Конфигурация повторных попыток:
interface RetryConfig {
enabled: boolean;
maxAttempts: number; // Максимум попыток
interval: number; // Интервал в мс
backoff: 'none' | 'linear' | 'exponential';
retryOn: string[]; // Типы ошибок для повтора
}Не блокируют выполнение.
Характеристики:
- Один вход и выход
- Без таймаута (fire and forget)
- Опциональная поддержка обратного вызова
interface AsyncActivity {
type: 'async';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [
{ id: 'output', type: 'flow' },
{ id: 'callback', type: 'event', optional: true }
];
};
properties: {
action: string;
arguments: ArgumentDefinition[];
waitForCompletion: boolean;
callbackTimeout?: number;
};
nested: false;
}Управляют исключениями.
Try-Catch
interface TryCatchActivity {
type: 'error-trycatch';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
tryBlock: Activity[]; // Активности блока try
catchBlocks: CatchDefinition[]; // Определения catch
finallyBlock?: Activity[]; // Активности блока finally
};
nested: true;
}
interface CatchDefinition {
id: string;
exceptionType: string; // например, 'ValueError', 'TimeoutError'
variable?: string; // Переменная для хранения исключения
handler: Activity[];
}Выброс исключения
interface ThrowActivity {
type: 'error-throw';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: []; // Нет выходов - выполнение останавливается
};
properties: {
exceptionType: string;
message: string;
};
nested: false;
}Вызывают повторно используемые процессы.
interface SubDiagramActivity {
type: 'subdiagram';
ports: {
inputs: [{ id: 'input', type: 'flow' }];
outputs: [{ id: 'output', type: 'flow' }];
};
properties: {
diagramId: string;
diagramName: string;
parameters: ParameterMapping[];
returns: ReturnMapping[];
timeout?: TimeoutConfig;
};
nested: false;
}type PortType =
| 'flow' // Поток управления (выполнение)
| 'data' // Поток данных (значения)
| 'event' // Выход события (async)
| 'error'; // Выход ошибки
interface Port {
id: string;
type: PortType;
label?: string;
required: boolean;
multiple?: boolean; // Может принимать несколько соединений
dataType?: string; // Для портов данных
position: 'left' | 'right' | 'top' | 'bottom';
}from rpaforge.sdk import activity, ActivityType, Port, Property
@activity(
type=ActivityType.SYNC,
name="Нажать элемент",
category="Веб автоматизация",
icon="🖱️",
description="Нажать на веб элемент",
timeout_default=30000,
)
class ClickElement:
"""Нажать на веб элемент с опциональным повтором."""
# Определить порты
input_ports = [
Port(id="input", type="flow", required=True)
]
output_ports = [
Port(id="output", type="flow", required=True),
Port(id="not_found", type="flow", required=False, label="Не найден")
]
# Определить свойства
properties = [
Property(
name="selector",
type="string",
required=True,
description="CSS селектор или XPath",
),
Property(
name="wait_time",
type="number",
default=5000,
description="Время ожидания элемента",
),
Property(
name="retry_count",
type="number",
default=3,
description="Количество попыток повтора",
),
]
def execute(self, context):
"""Выполнить активность."""
selector = context.get_property("selector")
wait_time = context.get_property("wait_time")
retry_count = context.get_property("retry_count")
for attempt in range(retry_count):
try:
element = context.wait_for_element(selector, wait_time)
element.click()
return context.output("output")
except ElementNotFoundError:
if attempt == retry_count - 1:
return context.output("not_found")
continue
return context.output("output")from rpaforge.sdk import ContainerActivity, ActivityType, Port, Property
@activity(
type=ActivityType.CONTAINER,
name="Приложение Excel",
category="Операции с данными",
icon="📊",
description="Работа с приложением Excel",
)
class ExcelApplication(ContainerActivity):
"""Контейнер для операций с Excel."""
input_ports = [Port(id="input", type="flow")]
output_ports = [Port(id="output", type="flow")]
properties = [
Property(
name="workbook_path",
type="string",
description="Путь к книге Excel",
),
Property(
name="visible",
type="boolean",
default=True,
description="Показать окно Excel",
),
Property(
name="auto_close",
type="boolean",
default=True,
description="Закрыть Excel при завершении",
),
]
def enter(self, context):
"""Вызывается при входе в контейнер."""
workbook_path = context.get_property("workbook_path")
visible = context.get_property("visible")
excel = context.create_excel_instance(visible=visible)
if workbook_path:
excel.open(workbook_path)
context.set_state("excel_instance", excel)
return True
def exit(self, context):
"""Вызывается при выходе из контейнера."""
excel = context.get_state("excel_instance")
if context.get_property("auto_close") and excel:
excel.close()
return Truefrom rpaforge.sdk import ActivityRegistry
# Зарегистрировать активности
registry = ActivityRegistry()
registry.register(ClickElement)
registry.register(ExcelApplication)
registry.register(ForEachRow)
registry.register(SwitchActivity)
registry.register(TryCatchActivity)
registry.register(RunProcess)
# Получить метаданные активности
metadata = registry.get_metadata("ClickElement")
print(metadata.to_dict())
# Список всех активностей по категориям
for category in registry.get_categories():
activities = registry.get_by_category(category)
print(f"{category}: {[a.name for a in activities]}")