From 02c7d3c6cfb3e3cfc7e1e36ccf6a4a790d868dea Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Mon, 31 Jul 2023 21:14:48 +0800 Subject: [PATCH 1/3] feat: `tap_cv` action supports ui type detection and tap --- docs/CHANGELOG.md | 8 +- hrp/pkg/uixt/action.go | 11 +++ hrp/pkg/uixt/ocr_vedem.go | 152 ++++++++++++++++++++++++++++++++- hrp/pkg/uixt/ocr_vedem_test.go | 14 +++ hrp/pkg/uixt/tap.go | 14 +++ 5 files changed, 195 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2116f3f4..dbf50fde 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,8 +1,14 @@ # Release History -## v4.3.6 (2023-07-24) +## v4.3.6 (2023-07-31) - feat: support to reset driver (or session only) automatically when UIA2 / WDA crashed or WebDriver request failed +- feat: `tap_cv` action supports ui type detection and tap +- compatibility: support indicating options separately in `MobileAction` level +- fix: use Override size if existed, otherwise use Physical size (android devices) +- fix: add default options for `swipe_to_tap_app` action +- refactor: ui validation methods +- fix: reuse the same request body during `GetImage` retry ## v4.3.5 (2023-07-23) diff --git a/hrp/pkg/uixt/action.go b/hrp/pkg/uixt/action.go index 8e8459ec..12ab0a01 100644 --- a/hrp/pkg/uixt/action.go +++ b/hrp/pkg/uixt/action.go @@ -479,6 +479,17 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { if imagePath, ok := action.Params.(string); ok { return dExt.TapByCV(imagePath, action.GetOptions()...) } + if uiParams, ok := action.Params.([]interface{}); ok { + var uiTypes []string + for _, uiParam := range uiParams { + uiType, ok := uiParam.(string) + if !ok { + continue + } + uiTypes = append(uiTypes, uiType) + } + return dExt.TapByUIDetection(uiTypes, action.Options.Options()...) + } return fmt.Errorf("invalid %s params: %v", ACTION_TapByCV, action.Params) case ACTION_DoubleTapXY: if location, ok := action.Params.([]interface{}); ok { diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 9c2982b8..b1e20d2c 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -54,9 +54,10 @@ func (o OCRResults) ToOCRTexts() (ocrTexts OCRTexts) { type ImageResult struct { imagePath string - URL string `json:"url"` // image uploaded url - OCRResult OCRResults `json:"ocrResult"` // OCR texts - LiveType string `json:"liveType"` // 直播间类型 + URL string `json:"url"` // image uploaded url + OCRResult OCRResults `json:"ocrResult"` // OCR texts + LiveType string `json:"liveType"` // 直播间类型 + UIResult UIResultMap `json:"uiResult"` // 图标检测 } type APIResponseImage struct { @@ -171,6 +172,11 @@ func newVEDEMImageService(actions ...string) (*veDEMImageService, error) { }, nil } +func (v *veDEMImageService) WithUITypes(uiTypes ...string) *veDEMImageService { + v.uiTypes = uiTypes + return v +} + // veDEMImageService implements IImageService interface // actions: // @@ -181,6 +187,7 @@ func newVEDEMImageService(actions ...string) (*veDEMImageService, error) { // close - get close popup type veDEMImageService struct { actions []string + uiTypes []string } func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageResult, err error) { @@ -189,6 +196,9 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR for _, action := range s.actions { bodyWriter.WriteField("actions", action) } + for _, uiType := range s.uiTypes { + bodyWriter.WriteField("uiTypes", uiType) + } bodyWriter.WriteField("ocrCluster", "highPrecision") formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png") @@ -401,3 +411,139 @@ func getRectangleCenterPoint(rect image.Rectangle) (point PointF) { } return point } + +func getCenterPoint(point PointF, width, height float64) PointF { + return PointF{ + X: point.X + width*0.5, + Y: point.Y + height*0.5, + } +} + +type UIResult struct { + Point PointF `json:"point"` + Width float64 `json:"width"` + Height float64 `json:"height"` +} + +func (u UIResult) Center() PointF { + return getCenterPoint(u.Point, u.Width, u.Height) +} + +type UIResults []UIResult + +func (u UIResults) FilterScope(scope AbsScope) (results UIResults) { + for _, uiResult := range u { + rect := image.Rectangle{ + Min: image.Point{ + X: int(uiResult.Point.X), + Y: int(uiResult.Point.Y), + }, + Max: image.Point{ + X: int(uiResult.Point.X + uiResult.Width), + Y: int(uiResult.Point.Y + uiResult.Height), + }, + } + + // check if ui result in scope + if len(scope) == 4 { + if rect.Min.X < scope[0] || + rect.Min.Y < scope[1] || + rect.Max.X > scope[2] || + rect.Max.Y > scope[3] { + // not in scope + continue + } + } + results = append(results, uiResult) + } + return +} + +type UIResultMap map[string]UIResults + +func (u UIResultMap) FilterUIResults(uiTypes []string) (uiResults UIResults, err error) { + var ok bool + for _, uiType := range uiTypes { + uiResults, ok = u[uiType] + if ok && len(uiResults) != 0 { + return + } + } + err = errors.Errorf("UI types %v not detected", uiTypes) + return +} + +func (u UIResults) GetUIResult(options ...ActionOption) (UIResult, error) { + actionOptions := NewActionOptions(options...) + + uiResults := u.FilterScope(actionOptions.AbsScope) + if len(uiResults) == 0 { + return UIResult{}, errors.Wrap(code.OCRTextNotFoundError, + "ui types not found in scope") + } + // get index + idx := actionOptions.Index + if idx < 0 { + idx = len(uiResults) + idx + } + + // index out of range + if idx >= len(uiResults) || idx < 0 { + return UIResult{}, errors.Wrap(code.OCRTextNotFoundError, + fmt.Sprintf("ui types index %d out of range", idx)) + } + return uiResults[idx], nil +} + +// newVEDEMUIService return image service for +func newVEDEMUIService(uiTypes []string) (*veDEMImageService, error) { + vedemUIService, err := newVEDEMImageService("ui") + if err != nil { + return nil, err + } + return vedemUIService.WithUITypes(uiTypes...), nil +} + +func (dExt *DriverExt) GetUIResultMap(uiTypes []string) (uiResultMap UIResultMap, err error) { + var bufSource *bytes.Buffer + var imagePath string + if bufSource, imagePath, err = dExt.takeScreenShot( + builtin.GenNameWithTimestamp("%d_ocr")); err != nil { + return + } + + vedemUIService, err := newVEDEMUIService(uiTypes) + if err != nil { + return + } + imageResult, err := vedemUIService.GetImage(bufSource) + if err != nil { + log.Error().Err(err).Msg("GetImage from ImageService failed") + return + } + + imageUrl := imageResult.URL + if imageUrl != "" { + dExt.cacheStepData.screenShotsUrls[imagePath] = imageUrl + log.Debug().Str("imagePath", imagePath).Str("imageUrl", imageUrl).Msg("log screenshot") + } + uiResultMap = imageResult.UIResult + return +} + +func (dExt *DriverExt) FindUIResult(uiTypes []string, options ...ActionOption) (point PointF, err error) { + uiResultMap, err := dExt.GetUIResultMap(uiTypes) + if err != nil { + return + } + uiResults, err := uiResultMap.FilterUIResults(uiTypes) + if err != nil { + return + } + uiResult, err := uiResults.GetUIResult(dExt.ParseActionOptions(options...)...) + point = uiResult.Center() + + log.Info().Interface("text", uiTypes). + Interface("point", point).Msg("FindUIResult success") + return +} diff --git a/hrp/pkg/uixt/ocr_vedem_test.go b/hrp/pkg/uixt/ocr_vedem_test.go index a026aafc..1004855a 100644 --- a/hrp/pkg/uixt/ocr_vedem_test.go +++ b/hrp/pkg/uixt/ocr_vedem_test.go @@ -72,3 +72,17 @@ func TestMatchRegex(t *testing.T) { } } } + +func TestTapUIWithScreenshot(t *testing.T) { + serialNumber := os.Getenv("SERIAL_NUMBER") + device, _ := NewAndroidDevice(WithSerialNumber(serialNumber)) + driver, err := device.NewDriver(nil) + if err != nil { + t.Fatal(err) + } + + err = driver.TapByUIDetection([]string{"dyhouse", "shoppingbag"}) + if err != nil { + t.Fatal(err) + } +} diff --git a/hrp/pkg/uixt/tap.go b/hrp/pkg/uixt/tap.go index 5d0ea39b..83603ccf 100644 --- a/hrp/pkg/uixt/tap.go +++ b/hrp/pkg/uixt/tap.go @@ -49,6 +49,20 @@ func (dExt *DriverExt) TapByCV(imagePath string, options ...ActionOption) error return dExt.TapAbsXY(point.X, point.Y, options...) } +func (dExt *DriverExt) TapByUIDetection(uiTypes []string, options ...ActionOption) error { + actionOptions := NewActionOptions(options...) + + point, err := dExt.FindUIResult(uiTypes, options...) + if err != nil { + if actionOptions.IgnoreNotFoundError { + return nil + } + return err + } + + return dExt.TapAbsXY(point.X, point.Y, options...) +} + func (dExt *DriverExt) Tap(param string, options ...ActionOption) error { return dExt.TapOffset(param, 0, 0, options...) } From edcfec46dcdfd0418d22e0fce34ebc537b7d8514 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Mon, 31 Jul 2023 21:22:19 +0800 Subject: [PATCH 2/3] rename: service_vedem.go and CV related error codes --- hrp/internal/code/code.go | 28 ++++++++-------- .../uixt/{ocr_vedem.go => service_vedem.go} | 32 +++++++++---------- ...cr_vedem_test.go => service_vedem_test.go} | 0 3 files changed, 29 insertions(+), 31 deletions(-) rename hrp/pkg/uixt/{ocr_vedem.go => service_vedem.go} (93%) rename hrp/pkg/uixt/{ocr_vedem_test.go => service_vedem_test.go} (100%) diff --git a/hrp/internal/code/code.go b/hrp/internal/code/code.go index e03420ac..0940eb9d 100644 --- a/hrp/internal/code/code.go +++ b/hrp/internal/code/code.go @@ -79,18 +79,16 @@ var ( MobileUIPopupError = errors.New("mobile UI popup error") // 78 ) -// OCR related: [80, 90) +// CV related: [80, 90) var ( - OCREnvMissedError = errors.New("OCR env missed error") // 80 - OCRRequestError = errors.New("OCR prepare request error") // 81 - OCRServiceConnectionError = errors.New("OCR service connect error") // 82 - OCRResponseError = errors.New("OCR parse response error") // 83 - OCRTextNotFoundError = errors.New("OCR text not found") // 84 - LoopActionNotFoundError = errors.New("loop action not found error") // 85 + CVEnvMissedError = errors.New("CV env missed error") // 80 + CVRequestError = errors.New("CV prepare request error") // 81 + CVServiceConnectionError = errors.New("CV service connect error") // 82 + CVResponseError = errors.New("CV parse response error") // 83 + CVResultNotFoundError = errors.New("CV result not found") // 84 + LoopActionNotFoundError = errors.New("loop action not found error") // 85 ) -// CV related: [90, 100) - var errorsMap = map[error]int{ // environment InvalidPython3Venv: 9, @@ -144,12 +142,12 @@ var errorsMap = map[error]int{ MobileUIPopupError: 78, // OCR related - OCREnvMissedError: 80, - OCRRequestError: 81, - OCRServiceConnectionError: 82, - OCRResponseError: 83, - OCRTextNotFoundError: 84, - LoopActionNotFoundError: 85, + CVEnvMissedError: 80, + CVRequestError: 81, + CVServiceConnectionError: 82, + CVResponseError: 83, + CVResultNotFoundError: 84, + LoopActionNotFoundError: 85, } func IsErrorPredefined(err error) bool { diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/service_vedem.go similarity index 93% rename from hrp/pkg/uixt/ocr_vedem.go rename to hrp/pkg/uixt/service_vedem.go index b1e20d2c..08d66851 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/service_vedem.go @@ -125,7 +125,7 @@ func (t OCRTexts) FindText(text string, options ...ActionOption) (result OCRText } if len(results) == 0 { - return OCRText{}, errors.Wrap(code.OCRTextNotFoundError, + return OCRText{}, errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("text %s not found in %v", text, t.texts())) } @@ -137,7 +137,7 @@ func (t OCRTexts) FindText(text string, options ...ActionOption) (result OCRText // index out of range if idx >= len(results) || idx < 0 { - return OCRText{}, errors.Wrap(code.OCRTextNotFoundError, + return OCRText{}, errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("text %s found %d, index %d out of range", text, len(results), idx)) } @@ -154,7 +154,7 @@ func (t OCRTexts) FindTexts(texts []string, options ...ActionOption) (results OC } if len(results) != len(texts) { - return nil, errors.Wrap(code.OCRTextNotFoundError, + return nil, errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("texts %s not found in %v", texts, t.texts())) } return results, nil @@ -203,21 +203,21 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png") if err != nil { - err = errors.Wrap(code.OCRRequestError, + err = errors.Wrap(code.CVRequestError, fmt.Sprintf("create form file error: %v", err)) return } size, err := formWriter.Write(imageBuf.Bytes()) if err != nil { - err = errors.Wrap(code.OCRRequestError, + err = errors.Wrap(code.CVRequestError, fmt.Sprintf("write form error: %v", err)) return } err = bodyWriter.Close() if err != nil { - err = errors.Wrap(code.OCRRequestError, + err = errors.Wrap(code.CVRequestError, fmt.Sprintf("close body writer error: %v", err)) return } @@ -233,7 +233,7 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR req, err = http.NewRequest("POST", env.VEDEM_IMAGE_URL, copiedBodyBuf) if err != nil { - err = errors.Wrap(code.OCRRequestError, + err = errors.Wrap(code.CVRequestError, fmt.Sprintf("construct request error: %v", err)) return } @@ -267,7 +267,7 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR time.Sleep(1 * time.Second) } if resp == nil { - err = code.OCRServiceConnectionError + err = code.CVServiceConnectionError return } @@ -275,13 +275,13 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR results, err := ioutil.ReadAll(resp.Body) if err != nil { - err = errors.Wrap(code.OCRResponseError, + err = errors.Wrap(code.CVResponseError, fmt.Sprintf("read response body error: %v", err)) return } if resp.StatusCode != http.StatusOK { - err = errors.Wrap(code.OCRResponseError, + err = errors.Wrap(code.CVResponseError, fmt.Sprintf("unexpected response status code: %d, results: %v", resp.StatusCode, string(results))) return @@ -293,7 +293,7 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR log.Error().Err(err). Str("response", string(results)). Msg("json unmarshal veDEM image response body failed") - err = errors.Wrap(code.OCRResponseError, + err = errors.Wrap(code.CVResponseError, "json unmarshal veDEM image response body error") return } @@ -312,14 +312,14 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageR func checkEnv() error { if env.VEDEM_IMAGE_URL == "" { - return errors.Wrap(code.OCREnvMissedError, "VEDEM_IMAGE_URL missed") + return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_URL missed") } log.Info().Str("VEDEM_IMAGE_URL", env.VEDEM_IMAGE_URL).Msg("get env") if env.VEDEM_IMAGE_AK == "" { - return errors.Wrap(code.OCREnvMissedError, "VEDEM_IMAGE_AK missed") + return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_AK missed") } if env.VEDEM_IMAGE_SK == "" { - return errors.Wrap(code.OCREnvMissedError, "VEDEM_IMAGE_SK missed") + return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_SK missed") } return nil } @@ -478,7 +478,7 @@ func (u UIResults) GetUIResult(options ...ActionOption) (UIResult, error) { uiResults := u.FilterScope(actionOptions.AbsScope) if len(uiResults) == 0 { - return UIResult{}, errors.Wrap(code.OCRTextNotFoundError, + return UIResult{}, errors.Wrap(code.CVResultNotFoundError, "ui types not found in scope") } // get index @@ -489,7 +489,7 @@ func (u UIResults) GetUIResult(options ...ActionOption) (UIResult, error) { // index out of range if idx >= len(uiResults) || idx < 0 { - return UIResult{}, errors.Wrap(code.OCRTextNotFoundError, + return UIResult{}, errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("ui types index %d out of range", idx)) } return uiResults[idx], nil diff --git a/hrp/pkg/uixt/ocr_vedem_test.go b/hrp/pkg/uixt/service_vedem_test.go similarity index 100% rename from hrp/pkg/uixt/ocr_vedem_test.go rename to hrp/pkg/uixt/service_vedem_test.go From 2610f023f54ca2ebe77b253ca087e4aa585e3591 Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Tue, 1 Aug 2023 13:58:55 +0800 Subject: [PATCH 3/3] add options for GetImage method --- docs/CHANGELOG.md | 2 +- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ext.go | 2 +- hrp/pkg/uixt/service_vedem.go | 67 ++++++++++++++--------------------- 4 files changed, 30 insertions(+), 43 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index dbf50fde..c6e4f3d9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## v4.3.6 (2023-07-31) +## v4.3.6-beta (2023-08-01) - feat: support to reset driver (or session only) automatically when UIA2 / WDA crashed or WebDriver request failed - feat: `tap_cv` action supports ui type detection and tap diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index e840b7c1..7476a89a 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.5 \ No newline at end of file +v4.3.6-beta \ No newline at end of file diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 99968628..3ce9e230 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -110,7 +110,7 @@ func NewDriverExt(device Device, driver WebDriver) (dExt *DriverExt, err error) return nil, err } - if dExt.ImageService, err = newVEDEMImageService("ocr", "upload", "liveType"); err != nil { + if dExt.ImageService, err = newVEDEMImageService(); err != nil { return nil, err } diff --git a/hrp/pkg/uixt/service_vedem.go b/hrp/pkg/uixt/service_vedem.go index 08d66851..b4d3ed41 100644 --- a/hrp/pkg/uixt/service_vedem.go +++ b/hrp/pkg/uixt/service_vedem.go @@ -160,21 +160,11 @@ func (t OCRTexts) FindTexts(texts []string, options ...ActionOption) (results OC return results, nil } -func newVEDEMImageService(actions ...string) (*veDEMImageService, error) { +func newVEDEMImageService() (*veDEMImageService, error) { if err := checkEnv(); err != nil { return nil, err } - if len(actions) == 0 { - actions = []string{"ocr"} - } - return &veDEMImageService{ - actions: actions, - }, nil -} - -func (v *veDEMImageService) WithUITypes(uiTypes ...string) *veDEMImageService { - v.uiTypes = uiTypes - return v + return &veDEMImageService{}, nil } // veDEMImageService implements IImageService interface @@ -185,19 +175,29 @@ func (v *veDEMImageService) WithUITypes(uiTypes ...string) *veDEMImageService { // liveType - get live type // popup - get popup windows // close - get close popup -type veDEMImageService struct { - actions []string - uiTypes []string -} +// ui - get ui position by type(s) +type veDEMImageService struct{} +type ( + actionOptions []string + uiTypeOptions []string +) -func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer) (imageResult ImageResult, err error) { +func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, options ...interface{}) (imageResult ImageResult, err error) { bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) - for _, action := range s.actions { - bodyWriter.WriteField("actions", action) - } - for _, uiType := range s.uiTypes { - bodyWriter.WriteField("uiTypes", uiType) + for _, option := range options { + switch ov := option.(type) { + case actionOptions: + for _, action := range ov { + bodyWriter.WriteField("actions", action) + } + case uiTypeOptions: + for _, uiType := range ov { + bodyWriter.WriteField("uiTypes", uiType) + } + default: + log.Warn().Interface("option", ov).Msgf("unexpected image service option") + } } bodyWriter.WriteField("ocrCluster", "highPrecision") @@ -338,7 +338,7 @@ func getLogID(header http.Header) string { type IImageService interface { // GetImage returns image result including ocr texts, uploaded image url, etc - GetImage(imageBuf *bytes.Buffer) (imageResult ImageResult, err error) + GetImage(imageBuf *bytes.Buffer, options ...interface{}) (imageResult ImageResult, err error) } // GetScreenResult takes a screenshot, returns the image recognization result @@ -346,11 +346,11 @@ func (dExt *DriverExt) GetScreenResult() (screenResult *ScreenResult, err error) var bufSource *bytes.Buffer var imagePath string if bufSource, imagePath, err = dExt.takeScreenShot( - builtin.GenNameWithTimestamp("%d_ocr")); err != nil { + builtin.GenNameWithTimestamp("%d_cv")); err != nil { return } - imageResult, err := dExt.ImageService.GetImage(bufSource) + imageResult, err := dExt.ImageService.GetImage(bufSource, actionOptions{"ocr", "upload", "liveType"}) if err != nil { log.Error().Err(err).Msg("GetImage from ImageService failed") return @@ -495,28 +495,15 @@ func (u UIResults) GetUIResult(options ...ActionOption) (UIResult, error) { return uiResults[idx], nil } -// newVEDEMUIService return image service for -func newVEDEMUIService(uiTypes []string) (*veDEMImageService, error) { - vedemUIService, err := newVEDEMImageService("ui") - if err != nil { - return nil, err - } - return vedemUIService.WithUITypes(uiTypes...), nil -} - func (dExt *DriverExt) GetUIResultMap(uiTypes []string) (uiResultMap UIResultMap, err error) { var bufSource *bytes.Buffer var imagePath string if bufSource, imagePath, err = dExt.takeScreenShot( - builtin.GenNameWithTimestamp("%d_ocr")); err != nil { + builtin.GenNameWithTimestamp("%d_cv")); err != nil { return } - vedemUIService, err := newVEDEMUIService(uiTypes) - if err != nil { - return - } - imageResult, err := vedemUIService.GetImage(bufSource) + imageResult, err := dExt.ImageService.GetImage(bufSource, actionOptions{"ui"}, uiTypeOptions(uiTypes)) if err != nil { log.Error().Err(err).Msg("GetImage from ImageService failed") return