Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][MIG] stock_vlm_mgmt_kardex: Migration to 16.0 #2104

Merged
merged 3 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions setup/stock_vlm_mgmt_kardex/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
86 changes: 86 additions & 0 deletions stock_vlm_mgmt_kardex/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
======================================
Kardex integration with stock_vlm_mgmt
======================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:cd51efe55ae6d7d00483d20033cb98572c7c06b4755e5b15ccdbe1077187f31f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github
:target: https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_vlm_mgmt_kardex
:alt: OCA/stock-logistics-warehouse
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-16-0/stock-logistics-warehouse-16-0-stock_vlm_mgmt_kardex
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&target_branch=16.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Integration with Kardex VLMs

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_vlm_mgmt_kardex%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Tecnativa

Contributors
~~~~~~~~~~~~

* `Tecnativa <https://www.tecnativa.com>`_:

* David Vidal

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-chienandalu| image:: https://github.com/chienandalu.png?size=40px
:target: https://github.com/chienandalu
:alt: chienandalu

Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-chienandalu|

This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_vlm_mgmt_kardex>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions stock_vlm_mgmt_kardex/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
14 changes: 14 additions & 0 deletions stock_vlm_mgmt_kardex/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Kardex integration with stock_vlm_mgmt",
"summary": "Light alternative for Kardex VLM integrations",
"version": "16.0.1.0.0",
"author": "Tecnativa, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"maintainers": ["chienandalu"],
"license": "AGPL-3",
"category": "Stock",
"depends": ["stock_vlm_mgmt"],
"data": [],
}
2 changes: 2 additions & 0 deletions stock_vlm_mgmt_kardex/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import stock_location
from . import kardex_request
159 changes: 159 additions & 0 deletions stock_vlm_mgmt_kardex/models/kardex_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Copyright 2022 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
import socket

_logger = logging.getLogger(__name__)

KARDEX_KEYS = [
"task_type",
"task_id",
"address",
"carrier",
"pos_x",
"pos_y",
"qty",
"info1",
"info2",
"info3",
"info4",
]


KARDEX_OPERATION_CODES = {
"release": "0",
"put": "3",
"get": "4",
"count": "5",
}


class KardexRequest:
"""
Interface to Kardex SVMs

To command tasks we send a tcp message to the SVM with this format

TASK_TYPE;TASK_ID;ADDRESS;CARRIER;POS_X;POS_Y;QTY;INFO1;INFO2;INFO3;INFO4<CR><LF>

Each task is enqueued in the SVM and every time it gets finished it returns a
response with a success or error code (0 or 101).

- TASK_TYPE is configured at the machine itself.
- TASK_ID would be a unique id we give to the task so we can identify it when
the JMIF server responds back.
- ADDRESS: Which device to call.
- CARRIER: The tray we want to call
- POSX, POSY: Show the user where the items are located inside the tray
- QTY: How many items to get from the SVM
- The INFO fields are optional to show the user usefull info for the pick.

An example call:
3;001;21;6;7;1;40;PICK002;MAT02;Capacitor 40 mV;Check they're ok<CR><LF>

The message will get to the SVM screen. When the user finishes, the SVM will respond
back with a success message. Notice that in this response, the quantity has been
changed by the user:
0;001;21;6;7;1;25;PICK002;MAT02;Capacitor 40 mV;Check they're ok<CR><LF>

If there'd be an error in the process we'd get an error:
101;001;21;6;7;1;40;PICK002;MAT02;Capacitor 40 mV;Check they're ok<CR><LF>

If we want to release the carriers, we can call the carrier 0
0;465;21;0;0;0;0;;;;<CR><LF>
"""

def __init__(self, ip, port, timeout=0, **options):
self.ip = ip
self.port = int(port)
self.timeout = timeout
self.ignore_response = options.get("ignore_response")

Check warning on line 70 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L67-L70

Added lines #L67 - L70 were not covered by tests

def parse_data(self, data):
"""Transforms csv single string into a dictionary that we can work with.
@param {string} data: semicolon separated values for the kardex payload format
@return {dict} those csv values transformed into key value pairs
"""
parsed_data = dict.fromkeys(KARDEX_KEYS, None)
try:
parsed_data.update({k: v for k, v in zip(KARDEX_KEYS, data.split(";"))})
except Exception:
_logger.debug(f"Exception parsing data: {data}")

Check warning on line 81 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L77-L81

Added lines #L77 - L81 were not covered by tests
if parsed_data.get("qty"):
# Strip dots
parsed_data["qty"] = parsed_data["qty"].replace(".", "")
return parsed_data

Check warning on line 85 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L84-L85

Added lines #L84 - L85 were not covered by tests

def prepare_data(self, data):
"""Transforms data dict into kardex csv data string
@param {dict} data
@return {string}
"""
# No support for floats :S
data["qty"] = int(float(data["qty"]))
values = [str(v) for v in data.values()]
return f"{';'.join(values)}\r\n"

Check warning on line 95 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L93-L95

Added lines #L93 - L95 were not covered by tests

def request_operation(self, data):
"""
@param {string|dict} we can handle either a dictionary with the KARDEX_KEYS
format or the csv formatted values in a string (it must end in \r\n)
@return {dict} with the reponse of the device on the matching task
"""
if isinstance(data, dict):
# We receive the task type with a common code. Transform into the Kardex one
data["task_type"] = KARDEX_OPERATION_CODES.get(data["task_type"], "0")
data = self.prepare_data(data)
_logger.info(f"Request: {data}")
operation_id = self.parse_data(data).get("task_id")

Check warning on line 108 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L105-L108

Added lines #L105 - L108 were not covered by tests
if not operation_id:
return
data = data.encode("utf-8")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as device:
try:
device.connect((self.ip, self.port))
except ConnectionRefusedError:
return {"code": "-1", "task_id": operation_id}
device.sendall(data)

Check warning on line 117 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L110-L117

Added lines #L110 - L117 were not covered by tests
# Open the request with the optional timeout so the thread isn't halted
# forever
if self.timeout:
device.settimeout(self.timeout)

Check warning on line 121 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L121

Added line #L121 was not covered by tests
# Default response
response = {"code": "0", "task_id": operation_id}

Check warning on line 123 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L123

Added line #L123 was not covered by tests
# TODO: This is uncomplete: when we do the request, we won't receive the
# response until the VLM user validates the operation from the device screen
# That's an unknown amount of time.
# And what if the system is reset in the meantime? We'd loose the original
# thread and the response would be missed.
# I guess that's why the c2c module has that proxy thing, so they can be
# more resilient on that regard or at least to not block the thread waiting
# for a response.
while True and not self.ignore_response:
# TBE: Will this response window be enough to get it in one shot?
try:
res = device.recv(1024).decode("utf-8")
_logger.info(res)
except socket.timeout:
response["code"] = "-3"
return response
response = self.parse_data(res)

Check warning on line 140 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L134-L140

Added lines #L134 - L140 were not covered by tests
# Deal with response codes. Default to unkown issue code
response["code"] = response.get("task_type", "-999")

Check warning on line 142 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L142

Added line #L142 was not covered by tests
# Code 101 for an issue that happens in the VLM hardware itself
if response["code"] == "101":
response["code"] = "-4"

Check warning on line 145 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L145

Added line #L145 was not covered by tests
# Task cancelled
if response["code"] == "107":
response["code"] = "-5"

Check warning on line 148 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L148

Added line #L148 was not covered by tests
# If it's not our operation we should keep trying
# TBE: But until when? This locks the current thread so the screen
# is indeed locked and the user can't do virtually anything about it
if response["task_id"] == operation_id:
break

Check warning on line 153 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L153

Added line #L153 was not covered by tests
if response["task_id"] is None:
# Empty response. Unknow reason error. Could be due to a shutdown
# of the JMIF service.
return {"code": "-2", "task_id": operation_id}
_logger.info(f"Response: {response}")
return response

Check warning on line 159 in stock_vlm_mgmt_kardex/models/kardex_request.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/kardex_request.py#L157-L159

Added lines #L157 - L159 were not covered by tests
26 changes: 26 additions & 0 deletions stock_vlm_mgmt_kardex/models/stock_location.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright 2023 Tecnativa - David Vidal
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models

from .kardex_request import KardexRequest


class StockLocation(models.Model):
_inherit = "stock.location"

vlm_vendor = fields.Selection(
selection_add=[
("kardex", "Kardex"),
],
)

def _kardex_vlm_connector(self) -> KardexRequest:
"""Wildcarded method to return our vendor specific connector"""
return KardexRequest

Check warning on line 19 in stock_vlm_mgmt_kardex/models/stock_location.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/stock_location.py#L19

Added line #L19 was not covered by tests

def send_vlm_request(self, data, **options):
"""The tray call in Kardex doesn't return anything, so we can release the
thread immediately"""
if self.vlm_vendor == "kardex" and self.env.context.get("vlm_tray_call"):
options["ignore_response"] = True
return super().send_vlm_request(data, **options)

Check warning on line 26 in stock_vlm_mgmt_kardex/models/stock_location.py

View check run for this annotation

Codecov / codecov/patch

stock_vlm_mgmt_kardex/models/stock_location.py#L25-L26

Added lines #L25 - L26 were not covered by tests
3 changes: 3 additions & 0 deletions stock_vlm_mgmt_kardex/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* `Tecnativa <https://www.tecnativa.com>`_:

* David Vidal
1 change: 1 addition & 0 deletions stock_vlm_mgmt_kardex/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Integration with Kardex VLMs
Loading
Loading