C# Scripting.
Mình đã đọc xong lõi driver và engine của ScadaWeb. Giờ mình xem cách đóng gói exe để chọn thư viện chạy script, rồi viết driver C# Scripting.
Mình đang viết driver CSharpScript (dùng Roslyn, script C# đọc/ghi tag hệ thống), sau đó làm hộp thoại soạn script và nối vào Home.razor.
Mình đã thêm driver CSharpScript vào ScadaWeb, nhưng chưa build được vì máy mình không có .NET SDK. Bạn chạy dotnet build giúp mình, nếu báo lỗi thì gửi lại dòng lỗi để mình sửa. Lần build đầu cần Internet để tải NuGet.
Cách dùng
- Ở tab Thiết bị & Tag, chọn Driver =
CSharpScriptrồi bấm "+ Thêm PLC" (cột IP không dùng). - Bấm nút 📝 Scripts trên dòng thiết bị để mở hộp thoại soạn script.
- Mỗi script là code C# chạy bằng Roslyn. Tag hệ thống (S7, OPC UA, Modbus, MQTT, IEC104...) dùng làm đầu vào và đầu ra:
if (Tag("V1-3-Sim.50.A2 control U Enable") == 0) return 0;
var sp = Tag("V1-3-Sim.50.A2 Setpoint U value");
WriteIfChanged("V1-3.50.3", sp); // chỉ ghi khi giá trị khác
return sp;Ví dụ trên là script mẫu trong ảnh của bạn viết lại bằng C#.
Trigger
Cyclechạy mỗi N ms.OnChangechạy khi một trong các tag nguồn đổi giá trị, và luôn chạy một lần khi khởi động. Tag do chính script ghi ra không kích hoạt lại script.- Không có trigger "SourceWrited" như phần mềm mẫu.
OnChangebắt cả thay đổi do người dùng ghi tay lẫn do PLC, không tách riêng hai loại này.
Phần chạy trên PC
- Mỗi script chạy từng lần một, không chồng nhau. Quá
MaxRuntimethì báo "treo". Tag()ném lỗi và dừng script nếu tag không tồn tại hoặc mất kết nối, nên không ghi bậy xuống PLC.- Script có
Memđể nhớ giữa các lần chạy, cùng các hàmRising,Falling,TON,Log. - Hộp thoại có sẵn Lưu và biên dịch (hiện lỗi theo dòng), chạy thử một lần, khung nhật ký, và nút tạo tag kết quả.
- Script được lưu trong
project.json(Devices[].Scripts).
Tag kết quả
- Nút "+ Tạo tag kết quả" tạo tag thuộc thiết bị CSharpScript với địa chỉ là tên script. Giá trị của tag chính là giá trị script
return, dùng được cho giao diện, alarm và trend. - Ghi vào tag này sẽ chạy script ngay một lần.
Lưu ý
- Script ghi được tag xuống PLC nên chỉ Admin mở được hộp thoại.
- Một script lặp vô hạn mà không gọi
Ct.ThrowIfCancellationRequested()sẽ chiếm một luồng cho đến khi khởi động lại chương trình. Sửa code rồi Lưu thì script được tạo lại từ đầu. - Mình tự đọc metadata của assembly trong bộ nhớ để Roslyn vẫn biên dịch được khi publish
PublishSingleFilebằngbuild.bat, nhưng chưa thử. Bạn test bước publish này riêng, vì exe cũng sẽ nặng thêm khoảng 10 MB do Roslyn.
File đã thay đổi
- Mới:
CSharpScriptDriver.cs,Components/ScriptDialog.razor. - Sửa:
Models.cs,Home.razor,ScadaWeb.csproj,app.css,README.md.
File: /mnt/user-data/outputs/ScadaWeb_CSharpScript.zip
\\\\\\\\\\\\\\
Cột Tên trong bảng Tag giờ là ô nhập sửa được. Mình chưa build thử vì máy mình không có .NET SDK, nên bạn build rồi gửi lại dòng lỗi nếu có. File zip này gồm luôn InternalMemory và C# Scripting từ các lần trước.
Cách dùng
- Sửa tên rồi Enter hoặc bấm ra ngoài.
- Tên mới không được trống, không trùng tag khác (không phân biệt hoa/thường) và không chứa ký tự
|. - Nếu tên bị từ chối, ô nhập quay về tên cũ và có dòng đỏ báo lý do phía trên bảng. Nếu thành công, dòng xanh cho biết đã cập nhật những chỗ nào.
Những nơi tự cập nhật theo tên mới
- Widget trên mọi màn hình, kể cả các đường của widget Trend.
- Lịch sử trend (SQLite và bộ nhớ), nên đồ thị không bị đứt.
- Alarm đang báo, nên không sinh alarm trùng.
- Link AdvanLink, ánh xạ IEC104 Slave, tag nguồn IEC61850 Slave (sửa luôn file
config.json). - Danh sách tag nguồn của script C#.
- Chuỗi
"tên cũ"viết nguyên văn trong code script.
Giới hạn
- Tên tag ghép từ biến trong code script (ví dụ
Tag("V1-" + x)) thì bạn phải tự sửa. - Lịch sử alarm đã đóng trong cơ sở dữ liệu giữ tên cũ, vì đó là tên tại thời điểm xảy ra.
- Tag đang chạy xung (nút Pulse) thì chưa đổi tên được, thử lại sau vài giây.
File thay đổi
ScadaEngine.cs: hàmRenameTag.History.cs: hàmRename.Iec61850Slave.cs: hàmRenameSource.Components/Home.razor,wwwroot/app.css,README.md.
File: /mnt/user-data/outputs/ScadaWeb_RenameTag.zip
\\\\\\\\\\\
Mình đang viết widget Popup (khối động cơ bấm mở bảng MAN/AUTO, RUN/ERROR, START/STOP) cho Blazor, đã đọc xong Tag_popup.cs.
Mình đã thêm phần tử Popup vào Toolbox (dưới TagTable), chuyển từ TagPopup trong Tag_popup.cs. Bản zip gồm cả TagList và TagTable. Mình chưa build được vì môi trường không có dotnet, nên bạn build thử rồi báo lỗi nếu có.
Tag_popup.cs có 4 control, nhưng ScadaWeb đã có sẵn loại tương đương cho 3 cái: TagCheckBit giống Switch, TagStatusDot giống Lamp, TagPulseButton giống Pulse. Nên mình chỉ làm mới TagPopup, bên trong tự có MAN/AUTO, đèn RUN/ERROR và START/STOP.
Cách dùng
- Kéo Popup vào canvas, khối mặc định ghi "ĐỘNG CƠ".
- Text là tên hiển thị trên khối và tiêu đề popup. Màu ON / Màu OFF (phần chung) là màu khối khi RUN = 1 / RUN = 0, tương ứng
RunOnColor/RunOffColor. - PROPERTIES có 6 ô chọn tag: MAN, AUTO, RUN, ERROR, START, STOP, lấy từ danh sách tag hệ thống. Ngoài ra chỉnh được màu ERROR, màu chữ khi RUN và khi dừng, thời gian giữ xung START/STOP (giây).
Khi chạy Runtime
- Khối có nền theo tag RUN, và viền đỏ dày khi tag ERROR = 1, giống
OnPaintcủa bản WinForms. - Bấm khối để mở bảng ngay dưới khối. Bấm lại khối, bấm ra ngoài hoặc nút ✕ để đóng (bản WinForms đóng khi mất focus).
- MAN/AUTO là checkbox: bấm thì ghi 1/0, ô luôn hiện đúng giá trị tag.
- Đèn RUN/ERROR sáng theo tag.
- START/STOP ghi 1 rồi tự về 0 sau thời gian đã đặt, chạy ngầm trong engine nên vẫn về 0 dù đóng trình duyệt. Nút sáng lên trong lúc đang xung.
- Cần quyền điều khiển mới ghi được. Người chỉ xem vẫn mở popup để xem trạng thái, các nút bị khoá.
- Đang thiết kế thì bấm khối chỉ để chọn hoặc kéo, không mở popup.
- Đổi tên tag ở tab Thiết bị & Tag thì 6 tag của popup tự đổi theo. Copy, Paste và Nhân bản giữ nguyên.
File đã sửa hoặc thêm
Components/PopupBox.razor(mới): vẽ khối, mở popup và ghi giá trị.Components/PopupProps.razor(mới): khung thuộc tính.Models.cs: thêm các trườngPop*trongWidgetvà cập nhậtClone().WidgetKinds.csvàComponents/WidgetBody.razor: khai báo và vẽ phần tử Popup.ScadaEngine.cs: đổi tên tag thì cập nhật 6 tag của popup.wwwroot/scada.js: thêmscada.popđể đóng popup khi bấm ra ngoài.wwwroot/app.cssvàREADME.md: thêm CSS.pop*và mục mô tả.
Chưa làm so với bản WinForms
- Hộp thoại đổi style riêng (
PopupStyleDialog), vì màu đã chỉnh trong PROPERTIES. - Ctrl+kéo để di chuyển và lưu cấu hình ra
tag_popup_bindings.txt. Web có kéo thả widget, ô X/Y/Rộng/Cao, và lưu chung vàoproject.json. - Nhãn MAN/AUTO/RUN/ERROR/START/STOP trong popup đang cố định, chưa đổi tên được.
Các file đã nằm trong bản zip (z93_04_10_2026_Popup_ScadaWeb.zip):
- z93_04_10_2026_Popup_ScadaWeb.zip
- \\\\\\\\\\
Mình đã đọc xong
tag_table.cs, giờ viết widget TagTable (bảng ma trận hàng × cột, mỗi ô gán 1 tag) cho Blazor và đưa vào Toolbox.Mình đã thêm phần tử TagTable vào Toolbox (dưới TagList), chuyển từ
tag_table.cs. Bản zip này đã gồm cả TagList làm lần trước. Mình chưa build được vì môi trường không códotnet, nên bạn build thử rồi báo lỗi nếu có.Cách dùng
- Kéo TagTable vào canvas, bảng mẫu có 3 cột × 4 hàng.
- Text trong PROPERTIES là tiêu đề bảng.
- PROPERTIES cho thêm, xoá, đổi tên và đổi thứ tự cột (◀ ▶) và hàng (▲ ▼). Bảng luôn giữ ít nhất 1 hàng và 1 cột, giống bản WinForms.
- Mỗi hàng có 1 ô chọn Tag cho từng cột, lấy từ danh sách tag hệ thống.
- Chỉnh được màu (tiêu đề, nền bảng, chữ tên hàng, nền và chữ ô giá trị), cỡ chữ, chiều cao tiêu đề và hàng, độ rộng cột tên hàng. Các cột dữ liệu chia đều phần còn lại.
Khi chạy Runtime
- Giá trị cập nhật theo nhịp làm tươi 0.5 s của engine.
- Ô mờ nghĩa là tag chưa đọc được. Ô chưa gán tag hiện
---. - Người có quyền điều khiển bấm ô để ghi. Tag Bool đảo 0/1, tag khác gõ giá trị rồi Enter (Esc để huỷ). Ô sai số hoặc ghi lỗi thì viền đỏ.
- Bản WinForms ghi bằng double-click, bản web dùng 1 click để dùng được trên điện thoại.
- Đổi tên tag ở tab Thiết bị & Tag thì các ô đang gán tự đổi theo. Copy, Paste và Nhân bản giữ nguyên cả bảng.
File đã sửa hoặc thêm
Components/TagTableBox.razor(mới): vẽ bảng và ghi giá trị.Components/TagTableProps.razor(mới): khung thuộc tính.Models.cs: thêmTtCol,TtRow, các trườngTt*trongWidget,TtInit()và cập nhậtClone().WidgetKinds.csvàComponents/WidgetBody.razor: khai báo và vẽ phần tử TagTable.ScadaEngine.cs: đổi tên tag thì cập nhật các ô.wwwroot/app.cssvàREADME.md: thêm CSS.ttb*và mục mô tả.
Chưa làm so với bản WinForms
- Kéo thả tag vào ô.
- Ctrl+kéo tiêu đề để di chuyển.
- Hộp thoại vị trí và kích thước.
- Lưu cấu hình ra file riêng.
Trên web đã có các tính năng tương đương: kéo thả widget, ô X/Y/Rộng/Cao, và lưu chung vào
project.json.Bạn muốn mình thêm kéo thả tag vào ô (cho TagList và TagTable) không?
Các file đã nằm trong bản zip (
z92_04_10_2026_TagTable_ScadaWeb.zip):- z92_04_10_2026_TagTable_ScadaWeb.zip
- \\\\\\\\\\
Mình đã đọc xong
TagGauge.cs(đồng hồ kim 270°, 3 vùng màu đỏ/vàng/xanh, vạch chia, kim mượt), giờ viết widget Gauge bằng SVG cho Blazor.Mình đã thêm phần tử Gauge vào Toolbox (dưới Popup), chuyển từ
TagGauge.cs. Bản zip gồm cả TagList, TagTable và Popup. Mình chưa build được vì môi trường không códotnet, nên bạn build thử rồi báo lỗi nếu có.Cách dùng
- Kéo Gauge vào canvas rồi gán Tag ở PROPERTIES, tag này là giá trị của đồng hồ.
- Chưa gán Tag thì hiện giá trị mẫu bằng 75% thang đo, giống mặc định 75 của bản WinForms. Tag chưa đọc được thì đồng hồ mờ.
- Thang đo Min/Max, số vạch chia lớn và số chữ số thập phân chỉnh ở PROPERTIES, tương ứng
MinValue,MaxValue,MajorTicks,DecimalPlaces. - Đơn vị:
%dính liền số, đơn vị khác cách 1 khoảng trắng, nhưDrawCenterText. - Vùng màu gồm điểm cuối vùng đỏ và vàng (tỉ lệ 0..1) cùng 3 màu.
- Chỉnh được màu vạch và số trên vành, kim, số giá trị, núm tâm và nền.
- Cỡ chữ số trên vành và số giá trị tính theo khung 200 × 200, nên tự co theo ô Rộng/Cao của widget.
Giữ đúng hình dáng bản WinForms
- Đồng hồ quét 270° từ góc 135°, vành màu có 3 vùng đỏ → vàng → xanh.
- Các vạch chia nằm bên trong vành, số nằm đè lên vành màu.
- Kim có đuôi ngắn và núm tròn viền trắng ở tâm. Số giá trị nằm dưới tâm.
- Kim trượt mượt bằng CSS transition. Tốc độ đuổi 0.15 tương đương khoảng 0.4 giây, ô "Tốc độ đuổi" chỉnh được.
- Giá trị cập nhật theo nhịp làm tươi 0.5 s của engine, nên kim chuyển từng nấc mượt chứ không chạy liên tục 15 ms như timer WinForms.
File đã sửa hoặc thêm
Components/GaugeBox.razor(mới): vẽ đồng hồ bằng SVG.Components/GaugeProps.razor(mới): khung thuộc tính.Models.cs: thêm các trườngG*trongWidgetvà cập nhậtClone().WidgetKinds.csvàComponents/WidgetBody.razor: khai báo và vẽ phần tử Gauge.wwwroot/app.cssvàREADME.md: thêm CSS.gau*và mục mô tả.ScadaEngine.cskhông cần sửa vì Gauge dùngTagchung của widget, nên đổi tên tag tự cập nhật theo.
Chưa làm so với bản WinForms
- Ctrl+kéo để di chuyển, nhân bản bằng menu chuột phải, và lưu vị trí ra
tag_gauge_bindings.txt. Web đã có kéo thả widget, Copy/Paste/Nhân bản, ô X/Y/Rộng/Cao, và lưu chung vàoproject.json. - Font chữ đang cố định kiểu Segoe UI, chỉ chỉnh cỡ chữ, chưa chọn được tên font.
Các file đã nằm trong bản zip (
z94_04_10_2026_Gauge_ScadaWeb.zip):- z94_04_10_2026_Gauge_ScadaWeb.zip

