AI control
Để mình xem cấu trúc project trước.
Mình đã thêm phần tử AI (khung chat) vào Toolbox, làm đơn giản theo đúng mẫu TagList/Gauge. Môi trường của mình không có dotnet nên chưa build thử. Bạn build giúp và báo mình nếu có lỗi nhé.
Cách dùng
- Đặt API key theo một trong hai cách:
- Tạo biến môi trường
ANTHROPIC_API_KEY. - Hoặc tạo file
ai_key.txtcạnhScadaWeb.exe, bên trong chỉ chứa key.
- Tạo biến môi trường
- Build, vào Designer, bấm AI trong Toolbox và kéo vào màn hình.
- Ở Runtime, gõ câu hỏi rồi bấm Enter hoặc nút Gửi. Ví dụ: "tag nào đang bất thường?", "xu hướng 1 giờ qua thế nào?".
AI nhận được gì
- Danh sách tag: tối đa 200 tag, mỗi tag gồm tên, kiểu, giá trị hiện tại và trạng thái (OK hoặc mất kết nối).
- Lịch sử 1 giờ: chỉ với các tag bật Trend (tối đa 30 tag), gồm số mẫu, giá trị đầu, cuối, min, max, trung bình và khoảng 12 điểm mẫu.
- Hội thoại: 10 lượt gần nhất, để hỏi tiếp được.
- Dữ liệu được gom ở phía server mỗi lần bạn hỏi, nên AI luôn thấy giá trị mới nhất.
Giới hạn cho đỡ tốn token
- Mỗi lần trả lời tối đa 1000 token.
- Model mặc định là
claude-sonnet-4-6. Muốn đổi thì đặt biến môi trườngAI_MODEL. - Chỉ tài khoản có quyền điều khiển mới gửi được câu hỏi.
Các file đã sửa
Components/AiChatBox.razor: file mới, chứa toàn bộ logic.WidgetKinds.cs: thêm một dòngAIvào Toolbox.Components/WidgetBody.razor: thêm nhánhcase "AI".wwwroot/app.css: thêm style cho khung chat.
Tên phần tử là "AI" chứ không phải tag riêng, nên không cần gán Tag. Nếu muốn AI vẽ lại giao diện từ danh sách tag như ghi chú đỏ trong ảnh, mình có thể làm ở bước sau.
Toolbox chưa có AI vì bản đang chạy của bạn vẫn là code cũ. Phần tử AI nằm trong code mình sửa, nên phải đưa code mới vào project rồi build lại.
AiChatBox.razor để ở thư mục ScadaWeb/Components/, cùng chỗ với GaugeBox.razor, TagListBox.razor.
Ngoài file đó, mình còn sửa 3 file nữa, và cả 3 đều cần có trong project:
| File | Đường dẫn | Việc đã sửa |
|---|---|---|
AiChatBox.razor | Components/ | File mới |
WidgetKinds.cs | thư mục gốc ScadaWeb/ | Thêm dòng new() { Name = "AI", ... } |
WidgetBody.razor | Components/ | Thêm case "AI": |
app.css | wwwroot/ | Thêm style khung chat |
Cách làm nhanh nhất: dùng luôn file ScadaWeb_AI.zip ở tin nhắn trước. Trong zip đã có đủ 4 file ở đúng chỗ. Bạn giải nén và ghi đè lên thư mục project hiện tại, hoặc mở thẳng project trong zip.
Sau đó:
- Build lại bằng
build.bathoặcdotnet build, rồi chạy lại ScadaWeb. - Trình duyệt bấm Ctrl+F5 để nạp lại CSS.
- Nút AI sẽ xuất hiện trong Toolbox, ngay trước Menu.
Nếu build báo lỗi, bạn gửi mình dòng lỗi để mình sửa. Mình chưa build thử được vì môi trường không có dotnet.
Mình đã kiểm tra lại code: Toolbox lấy danh sách từ WidgetKinds.cs (Home.razor dòng 194), và dòng AI đã nằm trong đó. Nên nhiều khả năng bạn đang chạy bản build cũ, chưa phải code mới.
File CAP_NHAT_AI.zip gồm 5 file đầy đủ, đã có cấu trúc thư mục sẵn:
CAP_NHAT/
WidgetKinds.cs
Program.cs
Components/AiChatBox.razor
Components/WidgetBody.razor
wwwroot/app.cssCách cập nhật
- Giải nén, copy 5 file này ghi đè vào đúng thư mục trong project
ScadaWeb/(giữ nguyênComponents/vàwwwroot/). - Tắt hẳn ScadaWeb đang chạy. Nếu chưa tắt, file
.exebị khoá và build sẽ không thay được. - Build lại bằng
build.bathoặcdotnet build. Xem kỹ cuối log có dòng "Build succeeded". Nếu có lỗi đỏ thì gửi mình. - Chạy lại bản vừa build. Project có
FolderProfile.pubxml(publish ra thư mục), nên dễ chạy nhầm.exetrong thư mục publish cũ. Nếu bạn quen chạy từ thư mục publish thì phải publish lại. - Trình duyệt bấm Ctrl+F5.
Cách kiểm tra đã chạy đúng bản mới: mở địa chỉ http://<địa chỉ-server>/ping. Bản mới trả về ScadaWeb v4 OK + AI. Nếu vẫn hiện ScadaWeb v4 OK thì đang chạy bản cũ.
Khi đúng bản, nút AI sẽ nằm ngay trước Menu trong Toolbox. Đừng quên đặt ANTHROPIC_API_KEY hoặc file ai_key.txt để chat hoạt động.Để test miễn phí, bạn dùng Google Gemini API qua Google AI Studio. Theo các trang mình tra, Gemini API có gói miễn phí cho project đang hoạt động, không cần thanh toán, nhưng có giới hạn tốc độ. Bạn có thể tạo key mà không cần nhập thông tin thanh toán. benchlmaiweekly
Lưu ý hai điều:
- Google đã ngừng công bố con số giới hạn cụ thể, bạn xem giới hạn thật của mình trong trang AI Studio.
- Request ở gói miễn phí có thể được Google dùng để cải thiện sản phẩm. Khi test thì không sao, nhưng đừng gửi dữ liệu nhà máy nhạy cảm.
Code gốc của mình chỉ gọi Claude (Anthropic), mà Anthropic không có gói API miễn phí. Vì vậy mình đã sửa AiChatBox.razor để dùng được cả hai loại key. Key bắt đầu bằng AIza thì code tự gọi Gemini (model mặc định gemini-2.5-flash), key khác thì gọi Claude. Mình chưa build thử được vì môi trường không có dotnet.
Cách lấy key
- Vào https://aistudio.google.com/apikey và đăng nhập tài khoản Google.
- Bấm Create API key, rồi copy key (dạng
AIzaSy...).
Cách đặt key (chọn một trong hai):
- Tạo file
ai_key.txtcạnhScadaWeb.exe, bên trong chỉ dán key. - Hoặc đặt biến môi trường
GEMINI_API_KEY. Nhớ tắt và mở lại ScadaWeb để nhận biến mới.
Cập nhật code: tải CAP_NHAT_AI.zip ở trên, giải nén và ghi đè 5 file vào project như lần trước, rồi build lại. Bản này thay thế bản zip cũ, nên dùng bản này.
Nếu chat báo lỗi 429, nghĩa là bạn đã chạm giới hạn miễn phí, chờ một lúc rồi hỏi lại. Nếu báo 404 về model, đặt biến môi trường AI_MODEL sang tên model khác đang có trong AI Studio, ví dụ gemini-2.5-flash-lite.Mình đã điền key vào project, và sửa luôn một chỗ để key của bạn chạy được. Mình chưa build thử được vì môi trường không có dotnet.
Đã làm
- Key bắt đầu bằng
AQ., không phảiAIza, nên code cũ sẽ nhận nhầm là key Anthropic. Mình đã sửa: chỉ key bắt đầu bằngsk-antmới gọi Claude, còn lại gọi Gemini. - Tạo file
ai_key.txtchứa key của bạn. - Sửa
ScadaWeb.csprojđể khi build hoặc publish, fileai_key.txttự được copy cạnh.exe.
Cập nhật: tải CAP_NHAT_AI.zip ở trên, giải nén và ghi đè vào project (giữ nguyên thư mục Components/ và wwwroot/). File ai_key.txt và ScadaWeb.csproj nằm ở thư mục gốc ScadaWeb/. Sau đó build lại, chạy lại, rồi Ctrl+F5. Nút AI sẽ nằm ngay trước Menu trong Toolbox.
Key này đã hiện trong cuộc chat, nên sau khi test xong bạn nên vào https://aistudio.google.com/apikey xoá key và tạo key mới. Đừng đưa ai_key.txt lên GitHub hay gửi cho người khác.
Nếu chat báo lỗi, bạn chụp dòng lỗi hiện trong khung gửi cho mình. Lỗi 429 là hết giới hạn miễn phí. Lỗi 404 hoặc 400 thường do tên model, lúc đó đặt biến môi trường AI_MODEL=gemini-2.5-flash-lite.
Lỗi này nghĩa là key và kết nối đã chạy tốt. Chỉ có tên model gemini-2.5-flash đã bị Google ngừng cho tài khoản mới.
Mình đã đổi model mặc định sang gemini-3.8-flash, đúng như Google gợi ý trong thông báo lỗi. Mình chưa kiểm tra được model này hoạt động ra sao.
Cách cập nhật: zip lần này chỉ có một file là Components/AiChatBox.razor. Bạn ghi đè vào ScadaWeb/Components/, build lại, chạy lại, rồi Ctrl+F5.
Nếu không muốn build lại: bạn có thể đặt biến môi trường AI_MODEL=gemini-3.8-flash, rồi tắt và mở lại ScadaWeb. Cách này chỉ có tác dụng nếu bản đang chạy đã là bản AI mới nhất. Bản đó đã có đoạn đọc AI_MODEL.
Nếu sau này Google lại đổi tên model và báo 404, bạn chỉ cần đổi AI_MODEL sang tên model mới theo thông báo lỗi, không phải sửa code.
1. Có cần add danh sách tag để AI hiểu không?
Không cần. Mỗi lần bạn bấm Gửi, code tự gom:
- Toàn bộ tag trong hệ thống (tối đa 200): tên, kiểu, giá trị hiện tại, trạng thái.
- Lịch sử 1 giờ của các tag có bật Trend (tối đa 30 tag).
Lỗi 503 trong ảnh là lỗi phía Google nên AI chưa nhận được gì. Để AI hiểu rõ hơn khi hỏi "động cơ có bất thường không":
- Đặt tên tag dễ hiểu, ví dụ
Motor_Temp,Motor_Current. AI chỉ biết ý nghĩa qua tên tag. - Bật Trend cho các tag quan trọng ở tab Thiết bị & Tag, để AI thấy xu hướng chứ không chỉ giá trị tức thời.
- Hỏi cụ thể, ví dụ "Motor_Temp 1 giờ qua có tăng bất thường không?".
2. Lỗi 503 xử lý thế nào?
Lỗi này là server Google đang quá tải tạm thời, không phải lỗi code hay key của bạn. Trước mắt bạn bấm Gửi lại sau vài giây là được. Mình cũng đã sửa code để tự xử lý:
- Gặp lỗi 503, 429 hoặc 500 thì tự thử lại tối đa 4 lần, mỗi lần chờ lâu hơn (2s, 4s, 6s).
- Từ lần thử thứ 3, code đổi sang model dự phòng
gemini-flash-latest, rồigemini-flash-lite-latest. Hai tên này mình chưa kiểm tra được, nếu sai tên thì lần thử đó sẽ lỗi 404 và hiện ra. - Nếu cả 4 lần đều thất bại, khung chat hiện thông báo ngắn "Máy chủ AI đang quá tải...", thay vì cả đoạn JSON.
Mình chưa build thử được. Zip lần này chỉ có một file Components/AiChatBox.razor. Bạn ghi đè vào ScadaWeb/Components/, build lại, chạy lại, rồi Ctrl+F5. Vì tự thử lại nên có lúc chờ khoảng 10 đến 15 giây mới có trả lời, đó là bình thường.AI đã có quyền ghi tag, chạy theo kế hoạch từng bước. Mình chưa build thử được vì môi trường không có dotnet.
Cách hoạt động
- Bạn ra lệnh bằng tiếng Việt, ví dụ: "internal_6_bool chạy 10s, dừng 5s, chạy 3s, dừng 8s, rồi nhấp nháy mỗi 4s một lần, đủ 8 lần thì dừng".
- AI trả về kế hoạch gồm các bước, mỗi bước là ghi một giá trị xuống tag rồi giữ N giây. Phần nhấp nháy AI tự triển khai thành 8 bước bật/tắt, mỗi bước 4s, rồi một bước cuối đưa về 0.
- Server ghi xuống tag đúng thời gian đó (
E.Write, giống nút Switch), nên tag trên màn hình đổi theo lệnh AI. - Khung chat báo "Đang chạy N bước", và báo lại khi xong.
Giao diện mới nằm ở thanh tiêu đề của khung chat:
- Cho AI điều khiển: ô tích, mặc định bật. Bỏ tích thì AI chỉ phân tích, không ghi tag.
- ■ Dừng lệnh: hiện khi đang chạy kế hoạch. Bấm là dừng ngay, tag giữ nguyên giá trị vừa ghi.
An toàn
- Chỉ ghi tag có trong hệ thống. Nếu AI đưa tên tag sai, không bước nào chạy và khung chat báo cảnh báo.
- Tag Bool chỉ nhận 0 hoặc 1. Mỗi kế hoạch tối đa 100 bước, mỗi bước tối đa 3600s.
- Chỉ tài khoản có quyền điều khiển mới dùng được (như các nút khác).
- Ra lệnh mới trong lúc đang chạy thì kế hoạch cũ bị huỷ và thay bằng kế hoạch mới.
- Nên kiểm tra kế hoạch trước khi chạy với thiết bị thật. AI có thể hiểu sai lệnh. Hiện AI có thể ghi mọi tag, kể cả tag của thiết bị thật, chưa có danh sách tag được phép. Nếu muốn giới hạn, mình có thể thêm danh sách đó.
Cập nhật: zip chứa 2 file là Components/AiChatBox.razor và wwwroot/app.css. Bạn ghi đè vào project, build lại, chạy lại, rồi Ctrl+F5.
Bạn tạo tag internal_6_bool (kiểu Bool, thiết bị InternalMemory), kéo một Lamp hoặc Switch gán tag đó lên màn hình để thấy tag đổi theo lệnh.

