From 8a68ed73ed60cf526e0bfc7a4bd1ff73b1b63df0 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 3 Nov 2023 19:05:18 +0100 Subject: [PATCH] [ADD] stock_vlm_mgmt_kardex: New module for 14.0 Integration with Kardex VLMs TT45739 --- stock_vlm_mgmt_kardex/README.rst | 86 ++++ stock_vlm_mgmt_kardex/__init__.py | 1 + stock_vlm_mgmt_kardex/__manifest__.py | 14 + stock_vlm_mgmt_kardex/models/__init__.py | 2 + .../models/kardex_request.py | 159 +++++++ .../models/stock_location.py | 26 ++ stock_vlm_mgmt_kardex/readme/CONTRIBUTORS.rst | 3 + stock_vlm_mgmt_kardex/readme/DESCRIPTION.rst | 1 + .../static/description/index.html | 426 ++++++++++++++++++ 9 files changed, 718 insertions(+) create mode 100644 stock_vlm_mgmt_kardex/README.rst create mode 100644 stock_vlm_mgmt_kardex/__init__.py create mode 100644 stock_vlm_mgmt_kardex/__manifest__.py create mode 100644 stock_vlm_mgmt_kardex/models/__init__.py create mode 100644 stock_vlm_mgmt_kardex/models/kardex_request.py create mode 100644 stock_vlm_mgmt_kardex/models/stock_location.py create mode 100644 stock_vlm_mgmt_kardex/readme/CONTRIBUTORS.rst create mode 100644 stock_vlm_mgmt_kardex/readme/DESCRIPTION.rst create mode 100644 stock_vlm_mgmt_kardex/static/description/index.html diff --git a/stock_vlm_mgmt_kardex/README.rst b/stock_vlm_mgmt_kardex/README.rst new file mode 100644 index 000000000..76ef26bb4 --- /dev/null +++ b/stock_vlm_mgmt_kardex/README.rst @@ -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/14.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-14-0/stock-logistics-warehouse-14-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=14.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 `_. +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * 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 `__: + +|maintainer-chienandalu| + +This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_vlm_mgmt_kardex/__init__.py b/stock_vlm_mgmt_kardex/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_vlm_mgmt_kardex/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_vlm_mgmt_kardex/__manifest__.py b/stock_vlm_mgmt_kardex/__manifest__.py new file mode 100644 index 000000000..bb10bdc21 --- /dev/null +++ b/stock_vlm_mgmt_kardex/__manifest__.py @@ -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": "14.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": [], +} diff --git a/stock_vlm_mgmt_kardex/models/__init__.py b/stock_vlm_mgmt_kardex/models/__init__.py new file mode 100644 index 000000000..8b19cd26b --- /dev/null +++ b/stock_vlm_mgmt_kardex/models/__init__.py @@ -0,0 +1,2 @@ +from . import stock_location +from . import kardex_request diff --git a/stock_vlm_mgmt_kardex/models/kardex_request.py b/stock_vlm_mgmt_kardex/models/kardex_request.py new file mode 100644 index 000000000..3b8578c7d --- /dev/null +++ b/stock_vlm_mgmt_kardex/models/kardex_request.py @@ -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 + + 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 + + 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 + + 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 + + If we want to release the carriers, we can call the carrier 0 + 0;465;21;0;0;0;0;;;; + """ + + 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") + + 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: + pass + if parsed_data.get("qty"): + # Strip dots + parsed_data["qty"] = parsed_data["qty"].replace(".", "") + return parsed_data + + 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" + + 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") + 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) + # Open the request with the optional timeout so the thread isn't halted + # forever + if self.timeout: + device.settimeout(self.timeout) + # Default response + response = {"code": "0", "task_id": operation_id} + # 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) + # Deal with response codes. Default to unkown issue code + response["code"] = response.get("task_type", "-999") + # Code 101 for an issue that happens in the VLM hardware itself + if response["code"] == "101": + response["code"] = "-4" + # Task cancelled + if response["code"] == "107": + response["code"] = "-5" + # 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 + 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 diff --git a/stock_vlm_mgmt_kardex/models/stock_location.py b/stock_vlm_mgmt_kardex/models/stock_location.py new file mode 100644 index 000000000..e4cace87e --- /dev/null +++ b/stock_vlm_mgmt_kardex/models/stock_location.py @@ -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 + + 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) diff --git a/stock_vlm_mgmt_kardex/readme/CONTRIBUTORS.rst b/stock_vlm_mgmt_kardex/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..94b6ba953 --- /dev/null +++ b/stock_vlm_mgmt_kardex/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Tecnativa `_: + + * David Vidal diff --git a/stock_vlm_mgmt_kardex/readme/DESCRIPTION.rst b/stock_vlm_mgmt_kardex/readme/DESCRIPTION.rst new file mode 100644 index 000000000..1b5f33424 --- /dev/null +++ b/stock_vlm_mgmt_kardex/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Integration with Kardex VLMs diff --git a/stock_vlm_mgmt_kardex/static/description/index.html b/stock_vlm_mgmt_kardex/static/description/index.html new file mode 100644 index 000000000..312429f11 --- /dev/null +++ b/stock_vlm_mgmt_kardex/static/description/index.html @@ -0,0 +1,426 @@ + + + + + + +Kardex integration with stock_vlm_mgmt + + + +
+

Kardex integration with stock_vlm_mgmt

+ + +

Beta License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runboat

+

Integration with Kardex VLMs

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

Current maintainer:

+

chienandalu

+

This module is part of the OCA/stock-logistics-warehouse project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ +