mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[IMP] add a proxy to communicate with the kardex server
This commit is contained in:
committed by
Guewen Baconnier
parent
bbeb506db3
commit
86e9e62306
@@ -1 +1,2 @@
|
||||
from . import models
|
||||
from . import controllers
|
||||
|
||||
@@ -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',
|
||||
|
||||
1
stock_vertical_lift/controllers/__init__.py
Normal file
1
stock_vertical_lift/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import main
|
||||
20
stock_vertical_lift/controllers/main.py
Normal file
20
stock_vertical_lift/controllers/main.py
Normal 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()
|
||||
12
stock_vertical_lift/data/ir_sequence.xml
Normal file
12
stock_vertical_lift/data/ir_sequence.xml
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
50
stock_vertical_lift/models/vertical_lift_command.py
Normal file
50
stock_vertical_lift/models/vertical_lift_command.py
Normal 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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
'stock_vertical_lift',
|
||||
],
|
||||
'website': 'https://www.camptocamp.com',
|
||||
'data': [],
|
||||
'data': [
|
||||
],
|
||||
'installable': True,
|
||||
'development_status': 'Alpha',
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from . import stock_location
|
||||
from . import vertical_lift_shuttle
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
175
stock_vertical_lift_kardex/proxy/kardex-proxy.py
Normal file
175
stock_vertical_lift_kardex/proxy/kardex-proxy.py
Normal 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)
|
||||
1
stock_vertical_lift_kardex/proxy/requirements.txt
Normal file
1
stock_vertical_lift_kardex/proxy/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
aiohttp
|
||||
102
stock_vertical_lift_kardex/proxy/test.py
Normal file
102
stock_vertical_lift_kardex/proxy/test.py
Normal 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()
|
||||
1
stock_vertical_lift_kardex/requirements.txt
Normal file
1
stock_vertical_lift_kardex/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
aiohttp
|
||||
Reference in New Issue
Block a user