- 前言
- 实用函数
- 总结
最近,接触的项目需要自动化操作SAP,所以对SAP的自动化脚本进行调查了。整理了一些有用函,用于创建SAP Session和自动化登录。
实用函数项目中使用的是Python语言,如果需要其他语言的版本可参考这里的函数。
import logging
import time
from contextlib import contextmanager
import pythoncom
import pywintypes
import win32com.client as client
import ZZK1LFI1122P0008A
LANGUAGES_WINDOW_TITLES = {
"EN": {
"MULTIPLE_LOGONS": "License Information for Multiple Logons",
"COPYRIGHT": "Copyright"
},
"ZH": {
"MULTIPLE_LOGONS": "多次登录许可证信息",
"Copyright": "Copyright"
},
"JA": {
"MULTIPLE_LOGONS": "多重ログオンに関するライセンス情報",
"COPYRIGHT": "Copyright"
}
}
LOGGER = logging.getLogger(__name__)
def create_sap_session(description, *, mandt="", name="", password="", language="EN"):
"""
创建一个新的SAP客户端窗口。
:param description: 登录描述字符串
:param mandt: 集团
:param name: 用户名
:param password: 密码
:param language: 客户端语言
:return: 返回SAP GuiSession对象
"""
try:
sap_gui_auto = client.GetObject("SAPGUI")
except pywintypes.com_error as _:
raise RuntimeError("检测到SAP客户端没有启动, 请启动SAP客户端后重试。")
application = sap_gui_auto.GetScriptingEngine
connection = application.OpenConnection(description)
# 使用第一个未登录的Session创建一个登录的Session
session = connection.Sessions(0)
session.findById("wnd[0]/usr/txtRSYST-MANDT").text = mandt
session.findById("wnd[0]/usr/txtRSYST-BNAME").text = name
session.findById("wnd[0]/usr/pwdRSYST-BCODE").text = password
session.findById("wnd[0]/usr/txtRSYST-LANGU").text = language
session.findById("wnd[0]").sendVKey(0)
# 有模态窗口
if session.Children.Count > 1:
wnd1 = session.findById("wnd[1]")
if wnd1.Text == LANGUAGES_WINDOW_TITLES[language]["MULTIPLE_LOGONS"]:
# 多用户登录是的确认选择。
session.findById("wnd[1]/usr/radMULTI_LOGON_OPT2").select()
session.findById("wnd[1]/tbar[0]/btn[0]").press()
# 有模态窗口
if session.Children.Count > 1:
wnd1 = session.findById("wnd[1]")
if wnd1.Text == LANGUAGES_WINDOW_TITLES[language]["COPYRIGHT"]:
session.findById("wnd[1]/tbar[0]/btn[0]").press()
return session
def remove_sap_session(session):
"""
关闭SAP客户端窗口。
:param index: 客户端的索引
"""
if session:
connection = session.Parent
# session.UnlockSessionUI()
connection.CloseSession(session.Id)
def get_sap_session():
"""
获取SAP操作Session。
使用第一个连接的创建脚本执行的Session,如果Session的数据大于一个时,表示有脚本正在执行。
如果是用户打开有多个Session或由于执行失败导致多个Session残留时,需手动关闭多余的Session。
也可以提供接口,关闭多余的Session。
这里一个Session就是一个SAP客户端窗口。
:param index: 客户端的索引
:return: 返回SAP GuiSession对象
"""
try:
sap_gui_auto = client.GetObject("SAPGUI")
except pywintypes.com_error as _:
raise RuntimeError("检测到SAP客户端没有启动, 请启动SAP客户端后重试。")
application = sap_gui_auto.GetScriptingEngine
# 连接数等于0时,表示没有用户登录SAP客户端
# 即index对应的连接上没有用户登录。
if application.Connections.Count < 1:
session = None
else:
# 每个连接都仅返回第一个Session。
session_count = application.Connections(0).Sessions.Count
# 使用第一个Session创建一个新的Session,用于执行脚本
# 如果Session数量大于一则表明有脚本正在执行(后面在尝试是否可以开更多的脚本)
if session_count > 1:
# session = application.Connections(index).Sessions(1)
# timeout = 3
# while timeout > 0 and session.Busy:
# print("The session is Busy.")
# time.sleep(0.5)
# timeout -= 0.5
# if timeout > 0:
# pass
# else:
raise ValueError("有脚本正在执行,请稍后重试。n如果确认没有脚本在执行,请关闭多余的窗口后重试。")
else:
application.Connections(0).Sessions(0).CreateSession()
# 等待新创建的Session可以访问。
# 设置超时时间
timeout = 3
while timeout > 0 and application.Connections(0).Sessions.Count <= 1:
time.sleep(0.1)
timeout -= 0.1
if timeout > 0:
session = application.Connections(0).Sessions(1)
else:
TimeoutError("创建执行脚本的Session失败")
return session
@contextmanager
def sap_session_context():
session = None
try:
pythoncom.CoInitialize()
session = get_sap_session()
if session is None:
# 未登录是自动登录
from login_info import LOGIN_INFO
session = create_sap_session("RPA-SAP", **LOGIN_INFO)
# session = get_sap_session()
# session.SuppressBackendPopups = True # 没有这个属性
# 最大化新创建的Session的主窗口
# session.LockSessionUI() # 这个不是一直锁住整个Session,没什么作用。
session.ActiveWindow.Maximize()
yield session
# 捕获异常可以明确判断出错原因
# except Exception as ex:
# print(ex)
finally:
if session:
connection = session.Parent
# session.UnlockSessionUI()
connection.CloseSession(session.Id)
pythoncom.CoUninitialize()
@contextmanager
def sap_transaction_context(session, transaction_code):
try:
session.StartTransaction(transaction_code)
yield
finally:
session.EndTransaction()
def sap_executor(handler):
def wrapper(*args, **kwargs):
with sap_session_context() as session:
return handler(session, *args, ** kwargs)
return wrapper
@sap_executor
def do_sap_task1(session, *args, **kwargs):
result = 0
# t1
ZZK1LFI1122P0008A(*args, **kwargs).get_data_with_context(session)
# ... 其他事务操作
return result
# 下面时sap_executor装饰器的使用示例
# 这里比较奇怪的是函数(do_sap_task1)声明有session参数,但是在调用时不能传递,
# 其实也没有可用的session参数值。
def do_task1(*args, **kwargs):
do_sap_task1(*args, **kwargs)
def do_task2(*args, **kwargs):
@sap_executor
def executor(session, *args, **kwargs):
result = 0
# t2
ZZK1LFI1122P0008A(*args, **kwargs).get_data_with_context(session)
# ... 其他事务操作
return result
return executor(*args, **kwargs)
@sap_executor
def do_login(session):
pass
if __name__ == '__main__':
# do_task1()
# do_task2()
do_login()
总结
使用上的函数方便测试和其他SAP脚本的作成。
仅供参考,如有帮助不胜荣幸,请关注,点赞。
如需转载请注明出处。



