Quản lý hộp IoT

Trang này dành cho các nhà phát triển bảo trì và cải tiến các ứng dụng IoT của Viindoo và phần mềm của Viindoo IoT Box. Trang giải thích các thành phần khớp với nhau thế nào, mỗi phía của giao thức giữa hộp và máy chủ của nó chờ đợi điều gì, cách chạy mã của hộp và các bài kiểm thử của nó mà không cần Raspberry Pi, cách thực hiện những thay đổi thường gặp, và cách một bản phát hành phần mềm của hộp tới được các hộp.

Mọi tên trên trang này là một module, một tệp hoặc một ký hiệu (symbol) mà bạn tìm được trong mã. Mã nguồn là căn cứ: khi trang này và mã nguồn khác nhau, hãy tin mã nguồn và sửa lại trang.

Để thiết lập và dùng hộp như khách hàng vẫn làm, hãy đọc hướng dẫn sử dụng trước: Thiết lập và quản lý hộp IoT và Dùng thiết bị IoT trong các ứng dụng Viindoo.

Các thành phần của cụm IoT

Cụm có ba thành phần: các ứng dụng IoT cài trong một cơ sở dữ liệu Viindoo, phần mềm chạy trên hộp, và, với các cơ sở dữ liệu chạy trên Viindoo Cloud, dịch vụ cấp cho hộp một tên riêng.

Thành phần

Chức năng

viin_iot

Nền tảng: bản ghi hộp và thiết bị, ghép nối, các route mà hộp gọi (controllers/main.py), hành động thiết bị và kết quả của chúng, nhật ký của hộp, trạng thái kết nối, và lớp truyền tải phía trình duyệt để tới hộp qua mạng nội bộ (static/src/js/viin_iot_device_proxy.js).

viin_iot_pos

Điểm bán hàng: máy in phiếu tính tiền, máy quét mã vạch, cân và màn hình khách hàng của một quầy, cùng các máy in phiếu chế biến của bếp và quầy bar.

viin_iot_mrp, viin_iot_mrp_workorder

Sản xuất: máy in tem nhãn, cân và bàn đạp của từng trung tâm sản xuất, trong lệnh công đoạn và trên màn hình Xưởng sản xuất (viin_mrp_workorder).

viin_iot_quality, viin_iot_quality_mrp, viin_iot_mrp_workorder_quality

Chất lượng: số đo đọc từ một thiết bị đo, trên một kiểm tra chất lượng, trên một kiểm tra của lệnh công đoạn và trên một bước Đo của màn hình Xưởng sản xuất (viin_mrp_workorder_quality).

viin_iot_saas

Chỉ trên Viindoo Cloud: chuyển yêu cầu Tên Viindoo của hộp tới Viindoo Cloud.

viin_hw_drivers, viin_hw_homepage

Lớp phần mềm của hộp, trong kho mã công khai Viindoo/iot-hw: lớp này khởi động các module hộp của core dưới sự kiểm soát của Viindoo, ghép nối hộp với máy chủ của nó, giữ hộp luôn được cập nhật và cung cấp trang của hộp.

box/ của cùng kho mã

Các tệp hệ thống của hộp, bộ cài lớp phần mềm, quy trình dựng ảnh hệ thống, các bước kiểm tra bản phát hành, và hộp phần mềm (software box) dùng cho phát triển.

Mọi module khác đều phụ thuộc viin_iot, trực tiếp hoặc qua một module cầu nối khác, và được cài tự động khi các module mà nó phụ thuộc đã được cài. Một model điều khiển thiết bị kế thừa viin.iot.device.consumer và khai báo trường thiết bị của nó trong _viin_iot_device_field; pos.config, pos.printer, mrp.workcenter, mrp.workorder và viin.quality.check đều làm vậy.

Ai gọi tới ai

  • Hộp gọi máy chủ của nó trên các route /iot/* của viin_iot: để ghép nối, để báo cáo chính nó và các thiết bị, để gửi nhật ký, để xác nhận một hành động đã xong và giá trị của một lần đọc, và để xin Tên Viindoo.

  • Máy chủ không bao giờ gọi tới hộp. Máy chủ đẩy lệnh thiết bị lên kênh bus riêng của hộp, kênh mà hộp lắng nghe qua kết nối trực tiếp (websocket) do hộp mở tới máy chủ.

  • Trình duyệt gọi tới hộp qua mạng nội bộ, trên các route thiết bị của core (/hw_drivers/event và /hw_drivers/action). Chỉ viin_iot_device_proxy.js gọi tên các route này; một module sử dụng thiết bị hỏi qua service viin_iot_device và không bao giờ tự dựng địa chỉ của hộp.

  • Một người mở trang của hộp tại địa chỉ của hộp, để ghép nối, thiết lập hộp và đọc phần tự kiểm tra.

Lớp phần mềm của hộp nằm trên mã hộp của core thế nào

Hộp chạy các module hộp của core mà không sửa đổi: hw_drivers, hw_escpos và hw_posbox_homepage, tại commit core mà bản phát hành chỉ định. Lớp Viindoo được nạp bao quanh chúng qua server_wide_modules, theo thứ tự này (box/install/install_layer.sh):

viin_hw_drivers,hw_drivers,hw_escpos,hw_posbox_homepage,viin_hw_homepage,web

Không module nào của lớp phần mềm cài được vào một cơ sở dữ liệu ('installable': False); chúng chỉ chạy trên hộp.

Cổng khởi động

viin_hw_drivers/__init__.py gọi boot.start(). Cổng khởi động trước hết xử lý tệp đặt lại trên thẻ nhớ, rồi giữ lại các luồng (thread) mà core khởi động khi được import, import core, áp dụng các điểm nối, nạp mọi tệp khác của lớp phần mềm và giữ chứng chỉ của hộp, rồi mới cho các luồng của core chạy. start không bao giờ phát sinh ngoại lệ: phần nào lỗi sẽ được ghi nhật ký với Viindoo layer integrity và được ghi nhận, và hộp khởi động mà không có phần đó. Nếu hw_drivers được nạp trước lớp phần mềm, không điểm nối nào được áp dụng và phần tự kiểm tra sẽ báo điều đó.

Điểm nối, không vá mã

Lớp phần mềm không bao giờ sửa một tệp của core. Mỗi chỗ nó can thiệp vào core là một điểm nối (seam), khai báo trong viin_hw_drivers/seams/ (và một điểm nối trong viin_hw_homepage/controllers/hw_posbox_homepage_lock.py, điểm nối đóng và khóa các route của core):

  • một Seam khai báo number (thứ tự của nó và tên của nó trong mọi báo cáo), target (ký hiệu của core) và feature (thứ hộp mất đi nếu điểm nối không còn đúng);

  • FunctionSeam bọc một hàm hoặc một phương thức và kiểm tra rằng tham số của nó vẫn đúng như khi viết hàm thay thế; PartsSeam áp dụng từng phần của nó một;

  • @register khai báo một điểm nối; seams.load import mọi tệp điểm nối, nên chỉ cần khai báo là đủ;

  • một điểm nối mà ký hiệu của nó chỉ tồn tại sau khi core đã nạp các handler thiết bị sẽ chỉ ra, trong after, điểm nối áp dụng nó khi đó; trước lúc ấy nó ở trạng thái deferred.

Khi ký hiệu của một điểm nối đã bị chuyển chỗ hoặc đổi dạng, chỉ điểm nối đó thất bại: tính năng của nó bị tắt, nhật ký và dòng Phần mềm của hộp của phần tự kiểm tra chỉ ra điểm nối nào, và hộp vẫn khởi động. seams.integrity() là trạng thái mà trang của hộp hiển thị.

Các tệp điểm nối được chia theo phần chúng can thiệp: startup.py (các luồng, dịch vụ mã ghép nối của core, kiểm tra cập nhật, chứng chỉ của nhà cung cấp), identity.py (địa chỉ, phiên bản và địa chỉ phần cứng mà hộp báo cáo), reporting.py (các báo cáo gửi máy chủ và kênh trực tiếp), trust.py (các kết nối tới máy chủ có xác minh), attestation.py (số đọc được xác nhận) và handlers.py (các tệp handler thiết bị, trang màn hình, phiếu trạng thái, chủ sở hữu thiết bị).

Kiểm tra điểm nối

viin_hw_drivers/boot.py khi chạy như một chương trình sẽ kiểm tra mọi điểm nối với core mà một tệp cấu hình chỉ định, và không khởi động gì. Nó in một dòng cho mỗi điểm nối: ok (kèm ghi chú khi điểm nối ở trạng thái deferred), hoặc FAILED kèm lý do và tính năng bị tắt. Nó thoát với mã 0 khi mọi điểm nối đều đúng, 1 khi có một điểm nối không đúng hoặc một phần của lớp phần mềm không được nạp, và 2 khi không import được core. Hãy chạy nó mỗi khi commit core thay đổi; CI chạy nó ở mọi thay đổi.

Con trỏ bản phát hành

box-release.json ở thư mục gốc của kho mã cho biết một commit của lớp phần mềm là bản phát hành nào:

Khóa

Ý nghĩa

release

Tên bản phát hành. Bản thử nghiệm (candidate) có tên là rc.<n> hoặc kết thúc bằng -rc.<n>; bản phát hành được hỗ trợ bắt đầu bằng một chữ số.

series

Dòng phiên bản của core mà bản phát hành chạy trên đó.

odoo_commit

Commit của core mà hộp chạy cùng lớp phần mềm này. Hộp chạy đúng cặp commit đó và không chạy mã nào khác.

min_image

Phiên bản ảnh hệ thống cũ nhất (N.N) mà bản phát hành chấp nhận. Hộp có ảnh hệ thống cũ hơn sẽ từ chối bản phát hành và báo rằng nó cần một ảnh hệ thống mới.

candidate

true với một bản dựng thử nghiệm, bản mà chỉ hộp được ghi từ một ảnh hệ thống thử nghiệm mới theo.

box/build/check_release.sh pointer kiểm tra tệp này ở phía dựng ảnh và viin_hw_drivers/update.py áp dụng cùng các quy tắc đó trên hộp: một quy tắc đổi ở bên này thì phải đổi ở bên kia.

Hộp chuyển sang một bản phát hành thế nào

viin_hw_drivers/update.py cho hộp theo một git ref của kho mã lớp phần mềm, gọi là kênh phát hành của hộp: nhánh box-release/<series>. Hộp chỉ chuyển sang đầu nhánh đó khi một tag box-<series>-<release> trỏ tới cùng commit đó, và con trỏ của commit đó ghi đúng bản phát hành đó, đúng dòng phiên bản, một ảnh hệ thống mà hộp có, và không phải bản thử nghiệm. Cả hai cây mã (core và lớp phần mềm) đều được tải về và kiểm tra trước khi cây nào được chuyển.

Bộ bảo vệ hoàn tác (box/system/usr/local/sbin/viin-box-guard) đếm số lần khởi động của một bản phát hành mới cho tới khi lớp phần mềm đánh dấu bản đó là tốt. Tới lần khởi động thứ ba mà vẫn chưa có dấu đó, nó đưa cả hai cây mã về như cũ và ghi nhận bản phát hành là hỏng; hộp không bao giờ thử lại một bản hỏng. Khi máy chủ của hộp chạy một dòng phiên bản khác với hộp, hộp xem kênh phát hành của dòng phiên bản của máy chủ trước, và đó là cách hộp theo máy chủ của nó lên một dòng phiên bản mới ngay tại chỗ.

Giao thức giữa hộp và máy chủ

Mỗi yêu cầu và mỗi câu trả lời dưới đây là một hợp đồng được viết ở cả hai phía. Một khóa, một kiểu dữ liệu hay một mã lý do chỉ được đổi ở cả hai phía hoặc không phía nào, trong cùng một bản phát hành của mỗi phía.

Route

Phía hộp

Phía máy chủ (viin_iot)

/iot/pair

viin_hw_drivers/pairing.py, pair

IoT.iot_pair, viin.iot.box.pair._viin_iot_redeem

/iot/setup

bộ quản lý thiết bị của core (Manager.send_alldevices), các điểm nối trong seams/reporting.py

IoT.send_alldevices, viin.iot.box._viin_iot_store_report

/iot/printer/status

trình điều khiển máy in của core (websocket_client.send_to_controller), qua kết nối có xác minh của seams/trust.py

IoT.printer_status, viin.iot.device.dispatch._viin_iot_apply_completion

/iot/reading

viin_hw_drivers/attestation.py, confirm

IoT.box_reading, viin.iot.device.dispatch._viin_iot_record_box_value

/iot/certificate

viin_hw_drivers/public_name.py

IoT.iot_certificate, viin.iot.box._viin_iot_public_name_answer

/iot/log

bộ gửi nhật ký của core

IoT.iot_log, viin.iot.box.iot_log

/iot/box/<identifier>/display_url

trình điều khiển màn hình của core

IoT.iot_box_display_url

/iot/get_handlers, /iot/keyboard_layouts

core

Trả lời với nội dung rỗng: máy chủ không cung cấp gói trình điều khiển nào và không lưu bố cục bàn phím nào.

Các route này là công khai: hộp không có người dùng. Hộp chứng minh chính nó bằng thông tin xác thực của nó (token) trên /iot/setup, /iot/reading và /iot/certificate, và bằng print_id của hành động trên /iot/printer/status; địa chỉ phần cứng, vốn in trên thiết bị, không chứng minh được gì. /iot/log tìm hộp chỉ bằng địa chỉ phần cứng, vì bộ gửi nhật ký của core không gửi gì khác: route này chỉ có thể thêm dòng nhật ký, và Chấp nhận nhật ký thiết bị tắt chúng cho từng hộp. /iot/pair và /iot/certificate không bao giờ phát sinh ngoại lệ: lỗi của máy chủ được ghi nhật ký và được trả lời server_error hoặc provider_error.

Ghép nối

Người tạo hộp IoT tạo một mã ghép nối dùng một lần trong Viindoo (viin.iot.box.pair.action_generate): 12 ký tự, dùng được trong 15 phút, chỉ hiển thị một lần, và chỉ lưu giá trị băm của mã. Liên kết ghép nối mang mã sau #pair=, phần của liên kết mà trình duyệt không bao giờ gửi tới máy chủ.

Hộp gửi pairing_code, iot_box (name, identifier, ip, version, serial_number), protocol và, khi hộp xác nhận số đọc của nó, confirms_readings. Máy chủ trả lời {'status': 'paired', 'token', 'box_name'} hoặc {'status': 'refused', 'reason'} với reason là một trong invalid_code, expired, already_used, identifier_exists hoặc server_error. Một lần từ chối không thay đổi gì và mã vẫn dùng được. Token được trả lời luôn là token do chính yêu cầu đó tạo ra, cho đúng bản ghi hộp đó.

Trên hộp, pair xác minh máy chủ https trước khi gửi mã. Chứng chỉ máy chủ mà không tổ chức cấp chứng chỉ công khai nào bảo đảm chỉ được chấp nhận sau khi một người đã xác nhận dấu vân tay của chính chứng chỉ đó, hoặc của tổ chức cấp chứng chỉ gốc mà người đó đưa cho hộp. Hộp lưu máy chủ và thông tin xác thực theo cách của core, rồi khởi động lại dịch vụ của nó.

Thông tin xác thực của hộp và mã cài đặt

token và kênh bus (iot_channel) của hộp là bí mật, chỉ Quản trị viên IoT đọc được. Ghép nối lại sẽ tạo token và kênh mới.

Mã cài đặt mà trang của hộp hỏi được tính từ token ở cả hai phía và không được lưu ở đâu: một HMAC-SHA256 của một nhãn cố định với khóa là token, cắt còn 8 ký tự thuộc bảng ký tự của mã ghép nối. Máy chủ tính mã trong _viin_iot_settings_code (viin_iot/models/viin_iot_box.py) và hộp tính trong settings_code (viin_hw_drivers/lock.py). Chỉ đổi nhãn, bảng ký tự hoặc độ dài ở cả hai phía hoặc không phía nào. Sau khi mở khóa bằng mã, trang của hộp nhận một bằng chứng mở khóa có hiệu lực 15 phút.

Đăng ký và trạng thái kết nối

Core báo cáo hộp và các thiết bị của nó trên /iot/setup kèm token; máy chủ trả lời kênh bus của hộp, hoặc False với một token mà nó không biết. Sau đó hộp đăng ký nghe kênh đó qua kết nối trực tiếp của nó. ir.websocket._subscribe (viin_iot/models/ir_websocket.py) ghi nhận hộp có kênh được đăng ký nghe là đã kết nối; khi socket đóng, lần liên lạc cuối của hộp được ghi lại. Tác vụ định kỳ IoT: Hiển thị các hộp ngừng báo cáo là bị ngắt kết nối (_viin_iot_sweep_presence) hiển thị là bị ngắt kết nối, cùng các thiết bị của nó, một hộp không giữ kết nối trực tiếp nào và không báo cáo lâu hơn thời gian ân hạn. Thời gian ân hạn là 120 giây, trừ khi tham số hệ thống viin_iot.box_presence_grace_seconds đặt khác.

Một loại thiết bị, kiểu kết nối hoặc loại máy in mà máy chủ không biết sẽ được lưu là rỗng, và giá trị gốc được đăng vào phần trao đổi (chatter) của thiết bị (_viin_iot_note_unrecognised).

Hành động thiết bị và trạng thái in

Một model sử dụng thiết bị gọi _viin_iot_action(device, payload) trên chính bản ghi của nó. Nền tảng kiểm tra rằng bản ghi đó dùng thiết bị đó, tạo một dòng viin.iot.device.dispatch với một print_id mới, và đẩy một thông điệp iot_action lên kênh của hộp. Máy in gửi print_id, device_identifier và iot_mac tới /iot/printer/status sau khi đã in; máy chủ tìm dòng dispatch theo print_id của nó và báo cho bản ghi qua _viin_iot_on_result với origin='completion'. Tác vụ định kỳ IoT: Báo cáo các hành động thiết bị chưa được xác nhận kết thúc một dispatch không nhận được trả lời kịp thời là Chưa xác nhận và báo cho bản ghi với origin='timeout': hộp không báo được thất bại, nên điều này không có nghĩa là hành động đã thất bại.

print_id của một hành động là thứ chứng minh hành động đã hoàn tất, nên trường này chỉ Quản trị viên IoT đọc được: model sử dụng thiết bị không bao giờ hiển thị hay ghi nhật ký giá trị này.

Số đọc và việc xác nhận số đọc

Một lần đọc không bao giờ đi qua bus. _viin_iot_request_value(device) mở một yêu cầu giá trị và không gửi gì; token của yêu cầu được đưa cho trình duyệt, trình duyệt đọc hộp với token đó dưới dạng viin_iot_request và chuyển lại giá trị nhận được. _viin_iot_on_result với origin='reading' chỉ chấp nhận giá trị đó cho đúng yêu cầu đang mở đó, và chỉ một lần.

Hộp xác nhận số đọc của nó (attests_readings trên bản ghi hộp) còn gửi giá trị tới /iot/reading trước khi trình duyệt nhận được. Điểm nối trên EventManager.device_changed (seams/attestation.py) chỉ làm việc này với lần đọc có yêu cầu xác nhận. Từ đó trở đi, máy chủ chỉ chấp nhận giá trị được chuyển lại khi hộp đã xác nhận đúng giá trị đó với đúng đơn vị đó, nên không thể bịa ra một giá trị trên đường đi qua trình duyệt.

Tên Viindoo

Hộp có thể nhận một tên công khai riêng kèm chứng chỉ, để trình duyệt tới được hộp mà không hiện cảnh báo về chứng chỉ. Hộp (public_name.py) gửi token, địa chỉ nội bộ ip và, khi xin chứng chỉ, một yêu cầu ký (csr) tới /iot/certificate; khóa riêng của hộp không bao giờ rời khỏi hộp. Các câu trả lời (unavailable, name, pending, issued, refused) và các khóa của chúng được liệt kê trong VIIN_IOT_PUBLIC_NAME_ANSWERS ở máy chủ và trong ANSWERS ở hộp.

viin_iot không liên hệ với ai: hook _viin_iot_public_name_request của nó trả lời no_provider. viin_iot_saas ghi đè hook đó và _viin_iot_public_name_release: trên một cơ sở dữ liệu chạy trên Viindoo Cloud, cơ sở dữ liệu chứng minh chính nó với Viindoo Cloud, chuyển yêu cầu của hộp đi và chuyển câu trả lời về, và trả lại tên khi hộp bị lưu trữ hoặc bị xóa, hoặc khi Tên Viindoo của hộp bị tắt. Ở nơi khác nó không thay đổi gì. Với mỗi hộp, máy chủ hỏi nhà cung cấp tên nhiều nhất một lần mỗi phút.

Phát triển và kiểm thử

Hộp phần mềm

box/dev/softbox/softbox.sh chạy mã thật của hộp, gồm lớp phần mềm và core, trong một container trên máy trạm Linux x86, với một cân và một máy quét mã vạch mô phỏng và một máy chủ in có hàng đợi máy in phiếu tính tiền, máy in tem nhãn và máy in văn phòng. SOFTBOX_CORE là đường dẫn tới một kho git của core đã checkout tại commit cần chạy. box/dev/softbox/README.md liệt kê mọi lệnh và tham số; các lệnh hay dùng là:

$ export SOFTBOX_CORE=/path/to/core
$ box/dev/softbox/softbox.sh build
$ box/dev/softbox/softbox.sh start
$ box/dev/softbox/softbox.sh logs --follow
$ box/dev/softbox/softbox.sh exec sh -c 'echo 1.250 > /var/lib/softbox/scale-weight'
$ box/dev/softbox/softbox.sh exec sh -c 'echo 8934567890123 >> /var/lib/softbox/scanner-scans'
$ box/dev/softbox/softbox.sh stop

Mọi lệnh in đều kết thúc trong /var/lib/softbox/prints/ bên trong container. Hộp phần mềm không có Wi-Fi, màn hình, thiết bị USB hay hệ thống init; hãy kiểm thử những thứ đó trên một hộp thật.

Cảnh báo

Không bao giờ khởi động mã của hộp trực tiếp trên máy trạm: core sẽ khởi động lại máy chủ web và gắn lại hệ thống tệp gốc của chính máy mà nó chạy trên đó.

Bộ kiểm thử của hộp và kiểm tra điểm nối

$ box/dev/softbox/softbox.sh suite
$ box/dev/softbox/softbox.sh suite test_pairing
$ box/dev/softbox/softbox.sh run python3 /home/pi/iot-hw/viin_hw_drivers/boot.py --config /home/pi/odoo.conf

suite chạy python3 -m unittest trong viin_hw_drivers/tests với core import được; tham số chọn một module, một lớp hoặc một phương thức, và một lần chạy không có bài kiểm thử nào chạy sẽ bị tính là thất bại. Các tệp kiểm thử được nạp như các module cấp cao nhất (test_pairing), và không có gì được import trước chúng.

Các câu chữ của trang hộp

Câu chữ của trang hộp nằm trong viin_hw_homepage/static/src/app/i18n/en.json và vi.json. box/build/check_page_texts.py kiểm tra rằng một khóa nhận cùng các {placeholders} ở cả hai tệp và không câu chữ nào của trang gọi tên nhà cung cấp của core, ấn bản trả phí của họ, trang web của họ hay một số hiệu dòng phiên bản:

$ python3 -I box/build/check_page_texts.py

Một khóa thiếu trong vi.json sẽ hiển thị bằng tiếng Anh, và công cụ kiểm tra không báo điều đó: hãy thêm mọi khóa vào cả hai tệp.

Các ứng dụng phía máy chủ

Mỗi ứng dụng IoT có các bài kiểm thử Python (tests/), các bài kiểm thử đơn vị Hoot (static/tests/*.test.js) và các tour (static/tests/tours/). Các bài kiểm thử Hoot chạy trong trình duyệt tại /web/tests, và trong một lần chạy kiểm thử thì qua WebSuite.test_unit_desktop của web. viin_iot đưa static/tests/tours/viin_iot_tour_box_wire.js vào gói tài nguyên của tour: trong một tour không có hộp nào, nên các yêu cầu gửi tới hộp được giữ lại hoặc được trả lời ngay tại đó thay vì rời khỏi trình duyệt.

Kiểm thử từng module riêng lẻ và cùng với các module được cài bên cạnh nó:

  • riêng lẻ, với --skip-auto-install, để không module cầu nối nào được cài tự động làm nó chạy được;

  • cài cùng nhau, như khách hàng nhận được: mọi module cầu nối đều được cài tự động, nên trọn bộ là cấu hình mặc định.

$ odoo-bin -d <new database> -i viin_iot_mrp --skip-auto-install --test-enable --stop-after-init
$ odoo-bin -d <new database> -i viin_iot_mrp_workorder_quality,viin_iot_pos --test-enable --stop-after-init

Tích hợp liên tục

  • Phần mềm của hộp: workflow box-checks chạy ở mọi pull request và mọi lần push lên một nhánh dòng phiên bản: con trỏ bản phát hành, cú pháp và lint của script, lint của workflow, câu chữ của trang, unit của dịch vụ hộp, bộ kiểm thử của hộp trong hộp phần mềm và kiểm tra điểm nối với commit core mà con trỏ chỉ định. Nhánh dòng phiên bản yêu cầu workflow này đạt trước khi merge, và workflow phát hành chạy lại nó trên commit được gắn tag.

  • Ứng dụng phía máy chủ: Runbot dựng và kiểm thử các ứng dụng IoT như mọi ứng dụng Viindoo khác.

Những thay đổi thường gặp

Thêm một loại thiết bị hoặc thay đổi cách thiết bị hoạt động

  • Trên hộp, thiết bị được điều khiển bởi các tệp handler: một interface tìm ra thiết bị, một driver vận hành nó. Không bao giờ sửa tệp handler của core trên hộp. Handler của Viindoo đặt trong viin_hw_drivers/iot_handlers/ (interfaces/ hoặc drivers/): lớp phần mềm nạp nó ngay sau các handler của core, và một lần cập nhật core không xóa nó. Lớp phần mềm xác minh các lời gọi tới máy khác mà một tệp handler thực hiện qua đúng tên urllib3 và không qua thư viện nào khác, nên lời gọi như vậy phải dùng urllib3. Để thay đổi việc một ký hiệu của core làm, hãy thêm một điểm nối.

  • Trên máy chủ, thêm giá trị mới vào selection của viin.iot.device (type, connection_type hoặc subtype); trước khi thêm, thiết bị hiển thị giá trị rỗng và một ghi chú trong phần trao đổi. Ứng dụng dùng thiết bị kế thừa viin.iot.device.consumer, gửi lệnh bằng _viin_iot_action, đọc giá trị bằng _viin_iot_request_value và xử lý ba giá trị origin trong _viin_iot_on_result trên chính model của nó.

  • Để kiểm thử, thêm một handler mô phỏng trong box/dev/softbox/iot_handlers/ khi thiết bị cần phần cứng, và một phép thử trên trang của hộp trong viin_hw_drivers/device_tests.py khi người lắp đặt cần thử được thiết bị ở đó.

Thêm một câu chữ vào trang của hộp

Thêm khóa với cùng các placeholder vào en.json và vi.json, dùng nó qua t(key, params) của i18n.js, rồi chạy check_page_texts.py. Viết cho người lắp đặt, dùng các từ của hướng dẫn sử dụng, và chỉ gọi tên sản phẩm của Viindoo.

Thêm một dòng tự kiểm tra

viin_hw_drivers/selftest.py dựng một dòng cho mỗi tầng mà hộp phụ thuộc vào, theo thứ tự của LINES. Một dòng là _line(id, state, reason, action, **details), với state là một trong ok, warning, failed, neutral hoặc pending. Thêm id vào LINES, một hàm tính ra dòng đó, lời gọi hàm đó trong _lines, và các câu chữ line.<id>, reason.<id>.<reason> và, khi người dùng phải xử lý, action.<action> vào cả hai tệp ngôn ngữ. Thêm trường hợp đó vào viin_hw_drivers/tests/test_selftest.py, và thêm dòng đó cùng ý nghĩa của nó vào trang xử lý sự cố của hướng dẫn sử dụng.

Thay đổi giao thức một cách an toàn

  • Thay đổi cả hai phía trong cùng một đợt việc: route và docstring của nó trong viin_iot, module của hộp gọi route đó, và các bài kiểm thử của cả hai.

  • Các hộp cũ vẫn chạy bản phát hành chúng đang có cho tới khi cập nhật, nên máy chủ phải tiếp tục trả lời được chúng. Thêm khóa ở dạng tùy chọn; không bao giờ đổi tên hay bỏ một khóa, một mã lý do hay một trạng thái mà một hộp đã phát hành đọc tới.

  • Hộp gửi protocol (PROTOCOL trong pairing.py và public_name.py) trên /iot/pair và /iot/certificate. Hiện máy chủ không đọc giá trị này ở đâu. Khi một thay đổi không thể giữ tương thích, hãy tăng PROTOCOL trên hộp và trả lời theo từng giá trị ở máy chủ.

  • Không bao giờ gửi hay ghi nhật ký một bí mật: mã ghép nối, token, kênh, print_id, một yêu cầu ký hay một địa chỉ thuộc mạng của khách hàng.

Chuyển sang một dòng phiên bản mới

Mỗi dòng phiên bản có kênh phát hành riêng (box-release/<series>), con trỏ riêng và commit core riêng. Để đưa lớp phần mềm lên một dòng phiên bản mới:

  1. Trỏ lại mọi điểm nối tới các ký hiệu của core mới, chạy kiểm tra điểm nối cho tới khi mọi điểm nối đều ok, và chạy bộ kiểm thử của hộp. Ở dòng phiên bản tiếp theo, core gộp các module hộp của nó vào iot_drivers: tên module trong CORE (viin_hw_drivers/seams/__init__.py), trong boot.py, trong server_wide_modules, trong các route mà module trang chủ đóng lại và trong các tệp mà bộ cài và các workflow đọc từ core (unit của dịch vụ, script dựng ảnh hệ thống, script điểm truy cập) đều thay đổi, và một số hàm đã chuyển sang tệp khác.

  2. Đối chiếu mọi route phía máy chủ với những gì mã hộp của core mới gửi: /iot/setup, bộ gửi nhật ký, trình điều khiển màn hình và trạng thái in là các yêu cầu của core, không phải của Viindoo.

  3. Đặt series, odoo_commit và min_image trong box-release.json. Tăng min_image khi bản phát hành mới cần thứ mà chỉ ảnh hệ thống mới có; những hộp đó sẽ được báo cần ghi ảnh hệ thống mới thay vì chuyển sang bản mới.

  4. Phát hành các bản thử nghiệm, rồi một bản phát hành được hỗ trợ, trên nhánh dòng phiên bản mới, và thêm nhánh dòng phiên bản mới vào các deployment ref của các môi trường phát hành sau khi nhánh đó đã tồn tại và đã được bảo vệ.

Hộp có máy chủ đã chuyển lên dòng phiên bản mới sẽ xem kênh phát hành của dòng phiên bản đó trước và chuyển sang đó ngay tại chỗ khi ảnh hệ thống của hộp đủ mới.

Phát hành phần mềm của hộp

Một bản phát hành được công bố bằng các tag và workflow của kho mã hộp; README.md của kho mã là quy trình đầy đủ. Tóm tắt:

  • Bản thử nghiệm: push một tag box-<series>-rc.<n> lên đầu nhánh làm việc. box-image dựng một ảnh hệ thống thử nghiệm và công bố nó dưới dạng pre-release được đánh dấu CANDIDATE. Hộp được ghi từ ảnh đó theo nhánh box-candidate/<series>; không hộp nào của khách hàng chọn được kênh đó.

  • Bản được hỗ trợ: một pull request đã được review đặt box-release.json (candidate là false, release mới, odoo_commit) trên nhánh dòng phiên bản; sau đó một người quản lý phát hành push tag box-<series>-<release> lên commit đã merge. box-image kiểm tra tag (gate), chạy các bước kiểm tra, dựng ảnh hệ thống, rồi publish tạo trang phát hành và advance chuyển box-release/<series>. Cả hai chạy trong môi trường box-release, môi trường cần chủ kho mã phê duyệt. Các hộp chỉ thấy bản phát hành khi advance kết thúc.

  • Hoàn tác: chạy workflow box-rollback từ nhánh dòng phiên bản với tag của một bản phát hành được hỗ trợ trước đó. Sau khi được phê duyệt, nó rút lại các trang của những bản phát hành mà kênh đang trỏ tới và trỏ kênh về bản trước. Các hộp áp dụng bản phát hành trước đó như mọi bản khác.

Một bản phát hành hay tag đã công bố không bao giờ bị thay đổi: cách sửa một bản phát hành hỏng là một bản phát hành mới. Không thứ gì trong kho mã của hộp, các workflow, các trang phát hành hay nhật ký của nó được chứa thông tin xác thực, tên máy chủ nội bộ, đường dẫn trên máy hay mã nguồn của một kho mã riêng tư.