tối ưu lưu trend cho tag
Mình giải nén bản mới và so với bản trước để đánh giá.
Chưa tối ưu xong. Bản z235 chỉ sửa được phần giao diện bảng tag. Phần engine và driver, vốn là nguyên nhân chính khiến tag nạp chậm và giá trị không realtime, hầu như chưa được nối vào. Mình đọc code chứ chưa chạy được (máy mình không có .NET SDK), nên các kết luận dưới đây dựa trên việc so với bản z227 và tìm chỗ gọi hàm.
Đã làm được
- Bảng “Thiết bị & Tag” chỉ vẽ 100–500 dòng mỗi trang, có ô tìm kiếm. Phần này giảm treo trình duyệt rõ rệt, vì trước đây Blazor phải vẽ và gửi diff của 100k dòng mỗi 0,5 giây.
AddTagsdùng HashSet nên hết O(n²).- Socket.IO có đường đọc
TryReadkhông ném exception, vàHistory.Addkhông còn đọc SQLite theo từng tag. - Save được gom lại (debounce).
- File
ScadaEnginePerf.cscó sẵn chỉ mục tag và cơ chế ghi lịch sử có chọn lọc.
Chưa làm hoặc chưa nối
OpcUaDriver.cskhông đổi. Mỗi tag mới gặp lần đầu vẫn buộc đọc lại toàn bộ tag đã đăng ký (dòngif (isNew || ...) Refresh(ses)). 100k tag nghĩa là khoảng 5 tỷ lần đọc node trong vòng đầu. Đây chính là lý do “phải add đủ tag mới thấy realtime” trong ảnh bạn gửi. Với driver OPC UA, vấn đề này vẫn còn nguyên.IBatchReaderđược khai báo nhưng không driver nào dùng, và engine cũng không gọi.- Hầu hết hook mới không có chỗ gọi.
ShouldRecord,RefreshTrendPens,GetTag,TagMap,HasAddress,ValueRevđều không được dùng, vàTagsChanged()không được gọi ở đâu cả. Hậu quả là engine vẫn ghi trend 1 điểm/giây × 8 giờ cho mọi tag. Với 100k tag đó là khoảng 2,9 tỷ điểm trong RAM, nên sẽ cạn bộ nhớ hoặc GC treo sau vài chục phút. PollDevicevẫn lọcP.Tags.Where(...)100k phần tử mỗi chu kỳ. Nó còn giữlock(sync)suốt cả vòng 100k tag, nên mọi thao tác UI cần lock này (ghi tag, tra tag, alarm) có thể bị đứng vài trăm ms mỗi chu kỳ.- Các widget (TagList, TagTable, TagMap, PluginBox, Popup, TrendChart) và
Writevẫn gọiP.Tags.ToArray()hoặcFirstOrDefaulttrên 100k phần tử mỗi 0,5 giây. - Cache bảng trong
Home.razorchỉ kiểm tra theo số lượng tag, nên đổi tên tag mà số lượng không đổi thì danh sách có thể cũ. - README nhắc
100k_patch.diffnhưng file này không có trong zip. - Bản vá chưa từng được biên dịch.
UI có lag hoặc treo không?
- Trang tag: chuyển tab và lướt trang sẽ mượt hơn nhiều so với trước.
- Chạy lâu hoặc khi nạp tag: vẫn lag và có nguy cơ treo vì các vấn đề engine ở trên. Ở thời điểm thêm 100k tag từ OPC UA, nạp vẫn rất chậm.
- Màn hình có widget dạng bảng tag: các widget này vẫn dựng từ điển 100k phần tử mỗi lần vẽ nên sẽ nặng.
Mình đang sửa dở trên bản gốc z227 và đã xong các phần sau: đọc OPC UA theo lô song song, chỉ mục tag, AddTags và import CSV không còn O(n²), Save gom lại, và ghi trend có chọn lọc. Mình chưa xong phần UI/widget/Home và chưa biên dịch được. Bạn muốn mình hoàn thiện trên bản z235 (giữ phần phân trang của nó) hay trên bản gốc? Mình khuyên dùng z235 làm nền, vì phần UI đã có sẵn, rồi bổ sung phần engine và OPC UA vào đó.
Mình đang nối nốt phần widget, Home và TagPicker vào bản z235 (đã xong engine và OPC UA).
Mình đã hoàn thiện trên bản z235, nhưng chưa biên dịch được vì máy mình không có .NET SDK. Mình chỉ kiểm tra bằng cách đọc code và đếm ngoặc (các file sửa có cùng số ngoặc với bản gốc). Bạn build trước, nếu có lỗi gửi lại thông báo, mình sửa tiếp.
Bản này giữ phần phân trang và tìm kiếm của z235, và bổ sung những chỗ nó còn thiếu:
- OPC UA (nguyên nhân chính khiến phải nạp đủ tag mới thấy realtime):
- Driver giờ đọc theo lô: dựng danh sách node một lần, chia lô 5000 node mỗi lệnh Read, đọc song song 4 lô. Lô nào về trước gán giá trị trước, nên giá trị hiện ngay từ lô đầu thay vì chờ đủ tag.
- Nếu server báo
BadTooManyOperations, driver tự giảm cỡ lô. - Hai tham số mới
BatchSizevàParallelhiện ở phần Edit thiết bị OPC UA. - Tag mới không còn buộc đọc lại cả lô như trước.
- Hộp thoại “Dò tag” khi bấm “Thêm tag đã chọn” cũng đọc kiểu dữ liệu theo lô, và chỉ hiện 300 dòng kết quả cho đỡ nặng.
- Engine:
- Vòng đọc gọi driver đọc theo lô và dùng danh sách tag đã nhóm sẵn theo thiết bị.
- Trend và lịch sử chỉ ghi cho tag bật Trend hoặc đang được widget Trend dùng khi dự án lớn hơn 3000 tag, nên RAM không còn phình theo 100k tag.
lock(sync)không còn giữ suốt vòng 100k tag.WritevàFindTagtra O(1).- Khi tắt chương trình hoặc xuất project, thay đổi đang chờ lưu được ghi nốt.
- Import CSV: 100k dòng không còn quét danh sách từng dòng.
- Widget và giao diện: TagList, TagTable, TagMap, Plugin, Popup, Trend và các chỗ tra tag trong Home dùng chỉ mục chung thay vì sao chép 100k tag mỗi lần vẽ. Ô chọn tag (TagPicker) cũng không dựng danh sách 100k tên mỗi lần gõ. Tab Trend cũ chỉ liệt kê 200 tag đầu.
- Chưa đổi: ô chọn tag dạng
<select>trong widget plugin (PlugField) vẫn liệt kê đủ tên. Nó chỉ nặng khi bạn mở thuộc tính của widget plugin. - Chưa kiểm chứng: mình chưa đo được thời gian chạy thực tế.
Sau khi build, bạn thử thêm 100k tag từ OPC UA và xem giá trị có hiện dần ngay không. Nếu server của bạn giới hạn số node mỗi lệnh Read, chỉnh BatchSize cho phù hợp.
Files are in /mnt/user-data/outputs:
- z236_ScadaWeb_100ktag.zip
Mình đã thêm chế độ lưu Trend để người dùng tự chọn, gói trong z237_ScadaWeb_100ktag_trend.zip (đây là bản đầy đủ). Mình chưa biên dịch được vì máy mình không có .NET SDK, nên bạn build rồi gửi lại lỗi nếu có.
Ở tab “Thiết bị & Tag”, phía trên bảng có dòng Lưu Trend với ba chế độ:
- Tự động: dự án từ 3000 tag trở xuống thì lưu hết, nhiều hơn thì chỉ lưu tag đã tick.
- Lưu TẤT CẢ tag: cột Trend hiện tick cho mọi tag và bị khoá.
- Chỉ lưu tag đã tick: bạn tick từng tag, hoặc dùng hai nút “Tick tất cả” và “Bỏ tick” theo bộ lọc tìm kiếm (ví dụ tìm theo thiết bị rồi tick cả nhóm).
Kế bên có ô Chỉ lưu khi giá trị thay đổi, mặc định bật. Cách hoạt động:
- Giá trị đổi thì ghi điểm mới. Giá trị đứng yên thì chỉ ghi một điểm mỗi 60 giây để đồ thị không bị đứt.
- Khi giá trị đổi sau một thời gian đứng yên, mình chèn thêm một điểm giữ giá trị cũ ngay trước đó, để đồ thị ra bậc thang thay vì đường chéo.
- Với database lâu dài, mình so với giá trị đã ghi lần gần nhất. Vì vậy một thay đổi xảy ra trong khoảng 10 giây sau lần ghi trước không bị mất, mà được ghi ở lần đọc kế tiếp.
Cả hai lựa chọn được lưu trong project.json, nên mở lại chương trình vẫn giữ nguyên.
Hai điểm bạn cần biết:
- Thời gian giữ trong RAM: chế độ “Lưu TẤT CẢ” với 100k tag sẽ tự rút ngắn thời gian giữ dữ liệu chi tiết trong RAM xuống còn 10 phút (dưới 300 tag thì giữ 8 giờ). Phần xa hơn đọc từ database, độ phân giải 10 giây.
- Tắt “chỉ lưu khi đổi”: nếu bạn tắt ô này khi lưu 100k tag, database sẽ phình rất nhanh. Giao diện có cảnh báo, và database tự xoá phần cũ nhất khi đạt 1 GB.
Mình cũng sửa một lỗi trong bản z235: xem đồ thị dài hơn thời gian giữ trong RAM thì chỉ ra phần ngắn. Giờ phần xa hơn được đọc từ database.

