[IMP] add a proxy to communicate with the kardex server

This commit is contained in:
Alexandre Fayolle
2019-11-14 10:57:42 +01:00
committed by Hai Lang
parent cf5848c2c3
commit 68e10cd503
11 changed files with 122 additions and 22 deletions

View File

@@ -1 +1,2 @@
from . import models
from . import controllers

View File

@@ -34,6 +34,7 @@
'views/stock_vertical_lift_templates.xml',
'views/shuttle_screen_templates.xml',
'security/ir.model.access.csv',
'data/ir_sequence.xml',
],
'installable': True,
'development_status': 'Alpha',

View File

@@ -0,0 +1 @@
from . import main

View File

@@ -0,0 +1,20 @@
import logging
import os
from odoo import http
from odoo.http import request
_logger = logging.getLogger(__name__)
class VerticalLiftController(http.Controller):
@http.route(['/vertical-lift'], type='http', auth='public', csrf=False)
def vertical_lift(self, answer, secret):
if secret == os.environ.get('VERTICAL_LIFT_SECRET', ''):
rec = request.env['vertical.lift.command'].sudo().record_answer(
answer
)
return str(rec.id)
else:
_logger.error('secret mismatch: %r != %r', secret, os.environ.get('VERTICAL_LIFT_SECRET', ''))
raise http.AuthenticationError()

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record model="ir.sequence" id="sequence_kardex_command">
<field name="name">Vertical Lift Commands</field>
<field name="code">vertical.lift.command</field>
<field name="prefix">L</field>
<field name="padding">6</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

View File

@@ -8,3 +8,4 @@ from . import stock_location
from . import stock_move
from . import stock_move_line
from . import stock_quant
from . import vertical_lift_command

View File

@@ -127,7 +127,7 @@ class StockLocation(models.Model):
)
return message
else:
return super()._hardware_vertical_lift_tray_payload(cell_location)
raise NotImplemented()
def fetch_vertical_lift_tray(self, cell_location=None):
"""Send instructions to the vertical lift hardware

View File

@@ -0,0 +1,50 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import api, exceptions, fields, models
_logger = logging.getLogger(__name__)
class VerticalLiftCommand(models.Model):
_name = 'vertical.lift.command'
_order = 'shuttle_id, name desc'
_description = "commands sent to the shuttle"
@api.model
def _default_name(self):
return self.env['ir.sequence'].next_by_code('vertical.lift.command')
name = fields.Char(
'Name', default=_default_name, required=True, index=True
)
command = fields.Char(required=True)
answer = fields.Char()
error = fields.Char()
shuttle_id = fields.Many2one('vertical.lift.shuttle', required=True)
@api.model
def record_answer(self, answer):
name = self._get_key(answer)
record = self.search([('name', '=', name)], limit=1)
if not record:
_logger.error('unable to match answer to a command: %r', answer)
raise exceptions.UserError('Unknown record %s' % name)
record.answer = answer
record.shuttle_id._hardware_response_callback(record)
return record
def _get_key(self, answer):
key = answer.split('|')[1]
return key
@api.model_create_multi
@api.returns('self', lambda value: value.id)
def create(self, vals_list):
for values in vals_list:
if "name" not in values:
name = self._get_key(values.get('command'))
if name:
values["name"] = name
return super().create(vals_list)

View File

@@ -38,7 +38,10 @@ class VerticalLiftShuttle(models.Model):
use_tls = fields.Boolean(
help="set this if the server expects TLS wrapped communication"
)
command_ids = fields.One2many(
'vertical.lift.command', 'shuttle_id',
string="Hardware commands"
)
_sql_constraints = [
(
"location_id_unique",
@@ -81,14 +84,21 @@ class VerticalLiftShuttle(models.Model):
If in hardware is 'simulation' then display a simple message.
Otherwise defaults to connecting to server:port using a TCP socket
(optionnally wrapped with TLS) and sending the payload, then waiting
for a response and disconnecting.
(optionnally wrapped with TLS) and sending the payload.
:param payload: a bytes object containing the payload
"""
self.ensure_one()
_logger.info('send %r', payload)
command_values = {
'shuttle_id': self.id,
'command': payload.decode(),
}
self.env['vertical.lift.command'].sudo().create(
command_values
)
if self.hardware == "simulation":
self.env.user.notify_info(message=payload,
title=_("Lift Simulation"))
@@ -102,28 +112,18 @@ class VerticalLiftShuttle(models.Model):
offset += size
if offset >= len(payload) or not size:
break
response = self._hardware_recv_response(conn)
_logger.info('recv %r', response)
return self._check_server_response(payload, response)
finally:
self._hardware_release_server_connection(conn)
def _hardware_recv_response(self, conn):
"""Default implementation expects the remote server to close()
the socket after sending the reponse.
Override to match the protocol implemented by the hardware.
def _hardware_response_callback(self, command):
"""should be called when a response is received from the hardware
:param conn: a socket connected to the server
:return: the response sent by the server, as a bytes object
:param response: a string
"""
response = b''
chunk = True
while chunk:
chunk = conn.recv(1024)
response += chunk
return response
success = self._check_server_response(command)
self._send_notification_refresh(success)
def _check_server_response(self, payload, response):
def _check_server_response(self, command):
"""Use this to check if the response is a success or a failure
:param payload: the payload sent
@@ -214,7 +214,7 @@ class VerticalLiftShuttle(models.Model):
self.mode = "inventory"
return self.action_open_screen()
def _send_notification_refresh(self):
def _send_notification_refresh(self, success):
"""Send a refresh notification to the current opened screen
The form controller on the front-end side will instantaneously
@@ -226,7 +226,8 @@ class VerticalLiftShuttle(models.Model):
The method is private only to prevent xml/rpc calls to
interact with the screen.
"""
self._operation_for_mode._send_notification_refresh()
# XXX do we want to do something special in the notification?
self._operation_for_mode()._send_notification_refresh()
class VerticalLiftShuttleManualBarcode(models.TransientModel):

View File

@@ -5,3 +5,4 @@ access_vertical_lift_operation_pick_stock_user,access_vertical_lift_operation_pi
access_vertical_lift_operation_put_stock_user,access_vertical_lift_operation_put stock user,model_vertical_lift_operation_put,stock.group_stock_user,1,1,1,1
access_vertical_lift_operation_put_line_stock_user,access_vertical_lift_operation_put_line stock user,model_vertical_lift_operation_put_line,stock.group_stock_user,1,1,1,1
access_vertical_lift_operation_inventory_stock_user,access_vertical_lift_operation_inventory stock user,model_vertical_lift_operation_inventory,stock.group_stock_user,1,1,1,1
access_vertical_lift_command,vertical_lift_command,model_vertical_lift_command,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_vertical_lift_operation_put_stock_user access_vertical_lift_operation_put stock user model_vertical_lift_operation_put stock.group_stock_user 1 1 1 1
6 access_vertical_lift_operation_put_line_stock_user access_vertical_lift_operation_put_line stock user model_vertical_lift_operation_put_line stock.group_stock_user 1 1 1 1
7 access_vertical_lift_operation_inventory_stock_user access_vertical_lift_operation_inventory stock user model_vertical_lift_operation_inventory stock.group_stock_user 1 1 1 1
8 access_vertical_lift_command vertical_lift_command model_vertical_lift_command base.group_user 1 0 0 0

View File

@@ -71,6 +71,18 @@
<field name="use_tls"/>
</group>
</group>
<group groups="base.group_no_one">
<label for="command_ids"/>
<field name="command_ids">
<tree>
<field name="name"/>
<field name="command"/>
<field name="answer"/>
<field name="error"/>
<field name="create_date"/>
</tree>
</field>
</group>
</form>
</field>
</record>