[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 Dũng (Trần Đình)
parent 18c6aa1dd3
commit ebcd872bb9
8 changed files with 336 additions and 21 deletions

View File

@@ -11,7 +11,8 @@
'stock_vertical_lift',
],
'website': 'https://www.camptocamp.com',
'data': [],
'data': [
],
'installable': True,
'development_status': 'Alpha',
}

View File

@@ -1,2 +1,3 @@
from . import stock_location
from . import vertical_lift_shuttle

View File

@@ -28,10 +28,10 @@ class StockLocation(models.Model):
x, y = '', ''
subst = {
'code': code,
'hostId': 'odoo',
'hostId': self.env['ir.sequence'].next_by_code('vertical.lift.command'),
'addr': shuttle.name,
'carrier': self.name,
'carrierNext': '',
'carrier': self.level,
'carrierNext': '0',
'x': x,
'y': y,
'boxType': '',
@@ -40,7 +40,7 @@ class StockLocation(models.Model):
'part': '',
'desc': '',
}
payload = message_template.format(subst)
payload = message_template.format(**subst)
return payload.encode('iso-8859-1', 'replace')
def _hardware_vertical_lift_tray_payload(self, cell_location=None):
@@ -83,4 +83,6 @@ class StockLocation(models.Model):
payload = self._hardware_kardex_prepare_payload()
_logger.debug("Sending to kardex: {}", payload)
# TODO implement the communication with kardex
super()._hardware_vertical_lift_tray_payload(cell_location=cell_location)
else:
payload = super()._hardware_vertical_lift_tray_payload(cell_location=cell_location)
return payload

View File

@@ -4,6 +4,24 @@
from odoo import api, models
JMIF_STATUS = {
0: 'success',
101: 'common error',
102: 'sequence number invalid',
103: 'machine busy',
104: 'timeout',
105: 'max retry reached',
106: 'carrier in use or undefined',
107: 'cancelled',
108: 'invalid user input data',
201: 'request accepted and queued',
202: 'request processing started / request active',
203: 'carrier arrived, maybe overwritten by code 0',
301: 'AO occupied with other try on move back (store / put)',
302: 'AO occupied with other try on fetch (pick)',
}
class VerticalLiftShuttle(models.Model):
_inherit = 'vertical.lift.shuttle'
@@ -13,19 +31,33 @@ class VerticalLiftShuttle(models.Model):
values += [('kardex', 'Kardex')]
return values
def _hardware_recv_response(self, conn):
# the implementation uses messages delimited with \r\n
response = b''
chunk = True
while chunk:
chunk = conn.recv(1)
response += chunk
if response.endswith(b'\r\n'):
break
return response
def _check_server_response(self, payload, response):
payload = payload.decode('iso-8859-1')
response = response.decode('iso-8859-1')
def _check_server_response(self, command):
response = command.answer
code, sep, remaining = response.partition('|')
return code == "0"
code = int(code)
if code == 0:
return True
elif 1 <= code <= 99:
command.error = 'interface error %d' % code
return False
elif code in JMIF_STATUS and code < 200:
command.error = '%d: %s' % (code, JMIF_STATUS[code])
return False
elif code in JMIF_STATUS and code < 300:
command.error = '%d: %s' % (code, JMIF_STATUS[code])
return True
elif code in JMIF_STATUS:
command.error = '%d: %s' % (code, JMIF_STATUS[code])
elif 501 <= code <= 999:
command.error = '%d: %s' % (code, 'MM260 Error')
elif 1000 <= code <= 32767:
command.error = '%d: %s' % (
code, 'C2000TCP/C3000CGI machine error'
)
elif 0xFF0 <= code == 0xFFF:
command.error = '%x: %s' % (
code, 'C3000CGI machine error (global short)'
)
elif 0xFFF < code:
command.error = '%x: %s' % (code, 'C3000CGI machine error (long)')
return False

View File

@@ -0,0 +1,175 @@
#!/usr/bin/python3
import argparse
import asyncio
import logging
import os
import ssl
import time
import aiohttp
_logger = logging.getLogger(__name__)
class KardexProxyProtocol(asyncio.Protocol):
def __init__(self, loop, queue, args):
_logger.info("Proxy created")
self.transport = None
self.buffer = b""
self.queue = queue
self.loop = loop
self.args = args
def connection_made(self, transport):
_logger.info("Proxy incoming cnx")
self.transport = transport
self.buffer = b""
def data_received(self, data):
self.buffer += data
_logger.info("Proxy: received %s", data)
if len(self.buffer) > 65535:
# prevent buffer overflow
self.transport.close()
def eof_received(self):
_logger.info("Proxy: received EOF")
if self.buffer[-1] != b"\n":
# bad format -> close
self.transport.close()
data = (
self.buffer.replace(b"\r\n", b"\n")
.replace(b"\n", b"\r\n")
.decode("iso-8859-1", "replace")
)
self.loop.create_task(self.queue.put(data))
self.buffer = b""
def connection_lost(self, exc):
self.transport = None
self.buffer = b""
class KardexClientProtocol(asyncio.Protocol):
def __init__(self, loop, queue, args):
_logger.info("started kardex client")
self.loop = loop
self.queue = queue
self.args = args
self.transport = None
self.buffer = b""
def connection_made(self, transport):
self.transport = transport
_logger.info("connected to kardex server %r", transport)
async def keepalive(self):
while True:
t = int(time.time())
msg = "61|ping%d|SH1-1|0|0||||||||\r\n" % t
await self.send_message(msg)
await asyncio.sleep(20)
async def send_message(self, message):
_logger.info("SEND %r", message)
message = message.encode("iso-8859-1")
self.transport.write(message)
async def process_queue(self):
while True:
message = await self.queue.get()
await self.send_message(message)
def data_received(self, data):
data = data.replace(b"\0", b"")
_logger.info("RECV %s", data)
self.buffer += data
if b"\r\n" in self.buffer:
msg, sep, rem = self.buffer.partition(b"\r\n")
self.buffer = rem
msg = msg.decode('iso-8859-1', 'replace').strip()
if msg.startswith('0|ping'):
_logger.info('ping ok')
else:
_logger.info('notify odoo: %s', msg)
self.loop.create_task(self.notify_odoo(msg))
def connection_lost(self, exc):
self.loop.stop()
async def notify_odoo(self, msg):
url = self.args.odoo_url + "/vertical-lift"
async with aiohttp.ClientSession() as session:
params = {'answer': msg, 'secret': self.args.secret}
async with session.post(url, data=params) as resp:
resp_text = await resp.text()
_logger.info(
'Reponse from Odoo: %s %s', resp.status, resp_text
)
def main(args, ssl_context=None):
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
loop = asyncio.get_event_loop()
queue = asyncio.Queue(loop=loop)
# create the main server
coro = loop.create_server(
lambda: KardexProxyProtocol(loop, queue, args),
host=args.host,
port=args.port
)
loop.run_until_complete(coro)
# create the connection to the JMIF client
if args.kardex_use_tls:
if ssl_context is None:
ssl_context = ssl.create_default_context()
else:
ssl_context = None
coro = loop.create_connection(
lambda: KardexClientProtocol(loop, queue, args),
host=args.kardex_host,
port=args.kardex_port,
ssl=ssl_context,
)
transport, client = loop.run_until_complete(coro)
loop.create_task(client.keepalive())
loop.create_task(client.process_queue())
loop.run_forever()
loop.close()
def make_parser():
listen_address = os.environ.get("INTERFACE", "0.0.0.0")
listen_port = int(os.environ.get("PORT", "7654"))
secret = os.environ.get("ODOO_CALLBACK_SECRET", "")
odoo_url = os.environ.get("ODOO_URL", "http://localhost:8069")
odoo_db = os.environ.get("ODOO_DB", "odoodb")
kardex_host = os.environ.get("KARDEX_HOST", "kardex")
kardex_port = int(os.environ.get("KARDEX_PORT", "9600"))
kardex_use_tls = (
False
if os.environ.get("KARDEX_TLS", "") in ("", "0", "False", "FALSE")
else True
)
parser = argparse.ArgumentParser()
arguments = [
("--host", listen_address, str),
("--port", listen_port, int),
("--odoo-url", odoo_url, str),
("--odoo-db", odoo_db, str),
("--secret", secret, str),
("--kardex-host", kardex_host, str),
("--kardex-port", kardex_port, str),
("--kardex-use-tls", kardex_use_tls, bool),
]
for name, default, type_ in arguments:
parser.add_argument(name, default=default, action="store", type=type_)
return parser
if __name__ == "__main__":
parser = make_parser()
args = parser.parse_args()
main(args)

View File

@@ -0,0 +1 @@
aiohttp

View File

@@ -0,0 +1,102 @@
import socket
import asyncio
import logging
import time
_logger = logging.getLogger('kardex.proxy')
logging.basicConfig(level=logging.DEBUG)
class KardexProxyProtocol(asyncio.Protocol):
def __init__(self, loop, queue):
_logger.info('Proxy created')
self.transport = None
self.buffer = b''
self.queue = queue
self.loop = loop
def connection_made(self, transport):
_logger.info('Proxy incoming cnx')
self.transport = transport
self.buffer = b''
def data_received(self, data):
self.buffer += data
_logger.info('Proxy: received %s', data)
if len(self.buffer) > 65535:
# prevent buffer overflow
self.transport.close()
def eof_received(self):
_logger.info('Proxy: received EOF')
if self.buffer[-1] != b'\n':
# bad format -> close
self.transport.close()
data = self.buffer.replace(b'\r\n', b'\n').replace(b'\n', b'\r\n').decode('iso-8859-1', 'replace')
task = self.loop.create_task(self.queue.put(data))
self.buffer = b''
print('toto', task)
def connection_lost(self, exc):
self.transport = None
self.buffer = b''
class KardexClientProtocol(asyncio.Protocol):
def __init__(self, loop, queue):
_logger.info('started kardex client')
self.loop = loop
self.queue = queue
self.transport = None
self.buffer = b''
def connection_made(self, transport):
self.transport = transport
_logger.info('connected to kardex server %r', transport)
async def keepalive(self):
while True:
t = int(time.time())
msg = '61|ping%d|SH1-1|0|0||||||||\r\n' % t
await self.send_message(msg)
await asyncio.sleep(5)
async def send_message(self, message):
_logger.info('SEND %s', message)
message = message.encode('iso-8859-1').ljust(1024, b'\0')
self.transport.write(message)
async def process_queue(self):
while True:
message = await self.queue.get()
await self.send_message(message)
def data_received(self, data):
data = data.replace(b'\0', b'')
_logger.info('RECV %s', data)
self.buffer += data
def connection_lost(self, exc):
self.loop.stop()
if __name__ == '__main__':
_logger.info('starting')
loop = asyncio.get_event_loop()
loop.set_debug(1)
queue = asyncio.Queue(loop=loop)
coro = loop.create_server(
lambda: KardexProxyProtocol(loop, queue),
port=3000,
family=socket.AF_INET
)
server = loop.run_until_complete(coro)
coro = loop.create_connection(lambda: KardexClientProtocol(loop, queue),
'localhost', 9600)
transport, client = loop.run_until_complete(coro)
print('%r' % transport)
loop.create_task(client.keepalive())
loop.create_task(client.process_queue())
_logger.info('run loop')
loop.run_forever()
loop.close()

View File

@@ -0,0 +1 @@
aiohttp