Initial commit: Industrial HMI system with component architecture
- Implement WebView2-based HMI frontend with React + TypeScript + Vite - Add C# .NET backend with WebSocket communication layer - Separate UI components into modular structure: * RecipePanel: Recipe selection and management * IOPanel: I/O monitoring and control (32 inputs/outputs) * MotionPanel: Servo control for X/Y/Z axes * CameraPanel: Vision system feed with HUD overlay * SettingsModal: System configuration management - Create reusable UI components (CyberPanel, TechButton, PanelHeader) - Implement dual-mode communication (WebView2 native + WebSocket fallback) - Add 3D visualization with Three.js/React Three Fiber - Fix JSON parsing bug in configuration save handler - Include comprehensive .gitignore for .NET and Node.js projects 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
53
.gitignore
vendored
Normal file
53
.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Visual Studio
|
||||
.vs/
|
||||
*.vsidx
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
x86/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
build/
|
||||
|
||||
# .NET
|
||||
*.dll
|
||||
*.exe
|
||||
*.pdb
|
||||
*.cache
|
||||
|
||||
# Node.js
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# React / Vite
|
||||
dist/
|
||||
dist-ssr/
|
||||
*.local
|
||||
.vite/
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
25
backend/HMIWeb.sln
Normal file
25
backend/HMIWeb.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36511.14 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HMIWeb", "HMIWeb\HMIWeb.csproj", "{33BFBC63-D007-4922-8412-99776B42A016}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{33BFBC63-D007-4922-8412-99776B42A016}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{33BFBC63-D007-4922-8412-99776B42A016}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{33BFBC63-D007-4922-8412-99776B42A016}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{33BFBC63-D007-4922-8412-99776B42A016}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6C31C8A9-B7A6-4B88-AC5C-A2F801C1F9D1}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
6
backend/HMIWeb/App.config
Normal file
6
backend/HMIWeb/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
</configuration>
|
||||
107
backend/HMIWeb/HMIWeb.csproj
Normal file
107
backend/HMIWeb/HMIWeb.csproj
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{33BFBC63-D007-4922-8412-99776B42A016}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>HMIWeb</RootNamespace>
|
||||
<AssemblyName>HMIWeb</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.3595.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3595.46\lib\net462\Microsoft.Web.WebView2.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.WinForms, Version=1.0.3595.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3595.46\lib\net462\Microsoft.Web.WebView2.WinForms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.Wpf, Version=1.0.3595.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3595.46\lib\net462\Microsoft.Web.WebView2.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MachineBridge.cs" />
|
||||
<Compile Include="WebSocketServer.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.3595.46\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.3595.46\build\Microsoft.Web.WebView2.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>이 프로젝트는 이 컴퓨터에 없는 NuGet 패키지를 참조합니다. 해당 패키지를 다운로드하려면 NuGet 패키지 복원을 사용하십시오. 자세한 내용은 http://go.microsoft.com/fwlink/?LinkID=322105를 참조하십시오. 누락된 파일은 {0}입니다.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.3595.46\build\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.3595.46\build\Microsoft.Web.WebView2.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
97
backend/HMIWeb/MachineBridge.cs
Normal file
97
backend/HMIWeb/MachineBridge.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace HMIWeb
|
||||
{
|
||||
// Important: Allows JavaScript to see this class
|
||||
[ClassInterface(ClassInterfaceType.AutoDual)]
|
||||
[ComVisible(true)]
|
||||
public class MachineBridge
|
||||
{
|
||||
// Reference to the main form to update logic
|
||||
private MainForm _host;
|
||||
|
||||
public MachineBridge(MainForm host)
|
||||
{
|
||||
_host = host;
|
||||
}
|
||||
|
||||
// --- Methods called from React (App.tsx) ---
|
||||
|
||||
public void MoveAxis(string axis, double value)
|
||||
{
|
||||
// In real app, call DLL here: Motion.Move(axis, value)
|
||||
Console.WriteLine($"[C#] Moving {axis} to {value}");
|
||||
|
||||
// For simulation, update host state directly
|
||||
_host.SetTargetPosition(axis, value);
|
||||
}
|
||||
|
||||
public void SetIO(int id, bool isInput, bool state)
|
||||
{
|
||||
Console.WriteLine($"[C#] Set IO {id} to {state}");
|
||||
_host.SetOutput(id, state);
|
||||
}
|
||||
|
||||
public void SystemControl(string command)
|
||||
{
|
||||
Console.WriteLine($"[C#] CMD: {command}");
|
||||
_host.HandleCommand(command);
|
||||
}
|
||||
|
||||
public void LoadRecipe(string recipeId)
|
||||
{
|
||||
Console.WriteLine($"[C#] Loading Recipe: {recipeId}");
|
||||
}
|
||||
|
||||
public string GetConfig()
|
||||
{
|
||||
// Generate 20 Mock Settings
|
||||
var settings = new System.Collections.Generic.List<object>();
|
||||
|
||||
// Core Settings
|
||||
settings.Add(new { Key = "Site Name", Value = "Smart Factory A-1", Group = "System Information", Type = "String", Description = "The display name of the factory site." });
|
||||
settings.Add(new { Key = "Line ID", Value = "L-2024-001", Group = "System Information", Type = "String", Description = "Unique identifier for this production line." });
|
||||
settings.Add(new { Key = "API Endpoint", Value = "https://api.factory.local/v1", Group = "Network Configuration", Type = "String", Description = "Base URL for the backend API." });
|
||||
settings.Add(new { Key = "Support Contact", Value = "010-1234-5678", Group = "System Information", Type = "String", Description = "Emergency contact number for maintenance." });
|
||||
settings.Add(new { Key = "Debug Mode", Value = "false", Group = "System Information", Type = "Boolean", Description = "Enable detailed logging for debugging." });
|
||||
settings.Add(new { Key = "Max Speed", Value = "1500", Group = "Motion Control", Type = "Number", Description = "Maximum velocity in mm/s." });
|
||||
settings.Add(new { Key = "Acceleration", Value = "500", Group = "Motion Control", Type = "Number", Description = "Acceleration ramp in mm/s²." });
|
||||
|
||||
// Generated Settings
|
||||
for (int i = 1; i <= 5; i++)
|
||||
{
|
||||
settings.Add(new {
|
||||
Key = $"Sensor_{i}_Threshold",
|
||||
Value = (i * 10).ToString(),
|
||||
Group = "Sensor Calibration",
|
||||
Type = "Number",
|
||||
Description = $"Trigger threshold for Sensor {i}."
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 3; i++)
|
||||
{
|
||||
settings.Add(new {
|
||||
Key = $"Safety_Zone_{i}",
|
||||
Value = "true",
|
||||
Group = "Safety Settings",
|
||||
Type = "Boolean",
|
||||
Description = $"Enable monitoring for Safety Zone {i}."
|
||||
});
|
||||
}
|
||||
|
||||
Console.WriteLine("get config (20 items)");
|
||||
return Newtonsoft.Json.JsonConvert.SerializeObject(settings);
|
||||
}
|
||||
|
||||
public void SaveConfig(string configJson)
|
||||
{
|
||||
Console.WriteLine($"[Backend] SAVE CONFIG REQUEST RECEIVED");
|
||||
Console.WriteLine($"[Backend] Data: {configJson}");
|
||||
// In a real app, we would save this to a file or database
|
||||
}
|
||||
}
|
||||
}
|
||||
48
backend/HMIWeb/MainForm.Designer.cs
generated
Normal file
48
backend/HMIWeb/MainForm.Designer.cs
generated
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace HMIWeb
|
||||
{
|
||||
partial class MainForm
|
||||
{
|
||||
/// <summary>
|
||||
/// 필수 디자이너 변수입니다.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 사용 중인 모든 리소스를 정리합니다.
|
||||
/// </summary>
|
||||
/// <param name="disposing">관리되는 리소스를 삭제해야 하면 true이고, 그렇지 않으면 false입니다.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form 디자이너에서 생성한 코드
|
||||
|
||||
/// <summary>
|
||||
/// 디자이너 지원에 필요한 메서드입니다.
|
||||
/// 이 메서드의 내용을 코드 편집기로 수정하지 마세요.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||
this.Name = "MainForm";
|
||||
this.Text = "Form1";
|
||||
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
158
backend/HMIWeb/MainForm.cs
Normal file
158
backend/HMIWeb/MainForm.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace HMIWeb
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
private Microsoft.Web.WebView2.WinForms.WebView2 webView;
|
||||
private Timer plcTimer;
|
||||
private WebSocketServer _wsServer;
|
||||
|
||||
// Machine State (Simulated PLC Memory)
|
||||
private double currX = 0, currY = 0, currZ = 0;
|
||||
private double targetX = 0, targetY = 0, targetZ = 0;
|
||||
private bool[] inputs = new bool[32];
|
||||
private bool[] outputs = new bool[32];
|
||||
private string systemState = "IDLE";
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeWebView();
|
||||
|
||||
// Start WebSocket Server for HMR/Dev
|
||||
try
|
||||
{
|
||||
_wsServer = new WebSocketServer("http://localhost:8081/", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Failed to start WebSocket Server (Port 8081). Run as Admin or allow port.\n" + ex.Message);
|
||||
}
|
||||
|
||||
// Set default inputs (Pressure OK, Estop OK)
|
||||
inputs[4] = true;
|
||||
inputs[6] = true;
|
||||
}
|
||||
|
||||
private async void InitializeWebView()
|
||||
{
|
||||
// 1. Setup Virtual Host (http://hmi.local) pointing to frontend/dist folder
|
||||
// Navigate up from bin/Debug/ to project root, then to frontend/dist
|
||||
string projectRoot = Path.GetFullPath(Path.Combine(Application.StartupPath, @"..\..\..\.."));
|
||||
string wwwroot = Path.Combine(projectRoot, @"frontend\dist");
|
||||
|
||||
this.Text = $"HMI Host - {wwwroot}";
|
||||
|
||||
webView = new Microsoft.Web.WebView2.WinForms.WebView2();
|
||||
webView.Dock = DockStyle.Fill;
|
||||
this.Controls.Add(webView);
|
||||
|
||||
await webView.EnsureCoreWebView2Async();
|
||||
|
||||
if (!Directory.Exists(wwwroot))
|
||||
{
|
||||
MessageBox.Show($"Could not find frontend build at:\n{wwwroot}\n\nPlease run 'npm run build' in the frontend folder.", "Frontend Not Found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
// Fallback to local wwwroot if needed, or just allow it to fail gracefully
|
||||
Directory.CreateDirectory(wwwroot);
|
||||
}
|
||||
|
||||
webView.CoreWebView2.SetVirtualHostNameToFolderMapping(
|
||||
"hmi.local",
|
||||
wwwroot,
|
||||
CoreWebView2HostResourceAccessKind.Allow
|
||||
);
|
||||
|
||||
// 2. Inject Native Object
|
||||
webView.CoreWebView2.AddHostObjectToScript("machine", new MachineBridge(this));
|
||||
|
||||
// 3. Load UI
|
||||
//webView.Source = new Uri("http://hmi.local/index.html");
|
||||
|
||||
// Disable default browser keys (F5, F12 etc) if needed for kiosk mode
|
||||
webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// --- Logic Loop ---
|
||||
private void PlcTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
// 1. Simulate Motion (Move Current towards Target)
|
||||
currX = Lerp(currX, targetX, 0.1);
|
||||
currY = Lerp(currY, targetY, 0.1);
|
||||
currZ = Lerp(currZ, targetZ, 0.1);
|
||||
|
||||
// 2. Prepare Data Packet
|
||||
var payload = new
|
||||
{
|
||||
type = "STATUS_UPDATE",
|
||||
sysState = systemState,
|
||||
position = new { x = currX, y = currY, z = currZ },
|
||||
ioState = GetChangedIOs() // Function to return array of IO states
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(payload);
|
||||
|
||||
// 3. Send to React via PostMessage (WebView2)
|
||||
if (webView != null && webView.CoreWebView2 != null)
|
||||
{
|
||||
webView.CoreWebView2.PostWebMessageAsJson(json);
|
||||
}
|
||||
|
||||
// 4. Broadcast to WebSocket (Dev/HMR)
|
||||
_wsServer?.Broadcast(json);
|
||||
}
|
||||
|
||||
private List<object> GetChangedIOs()
|
||||
{
|
||||
// Simply return list of all active IOs or just send all for simplicity
|
||||
var list = new List<object>();
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
list.Add(new { id = i, type = "input", state = inputs[i] });
|
||||
list.Add(new { id = i, type = "output", state = outputs[i] });
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
// Setup Simulation Timer (50ms)
|
||||
plcTimer = new Timer();
|
||||
plcTimer.Interval = 50;
|
||||
plcTimer.Tick += PlcTimer_Tick;
|
||||
plcTimer.Start();
|
||||
}
|
||||
|
||||
// --- Helper Methods called by Bridge ---
|
||||
public void SetTargetPosition(string axis, double val)
|
||||
{
|
||||
if (axis == "X") targetX = val;
|
||||
if (axis == "Y") targetY = val;
|
||||
if (axis == "Z") targetZ = val;
|
||||
}
|
||||
|
||||
public void SetOutput(int id, bool state)
|
||||
{
|
||||
if (id < 32) outputs[id] = state;
|
||||
}
|
||||
|
||||
public void HandleCommand(string cmd)
|
||||
{
|
||||
systemState = (cmd == "START") ? "RUNNING" : (cmd == "STOP") ? "PAUSED" : "IDLE";
|
||||
}
|
||||
|
||||
private double Lerp(double a, double b, double t) => a + (b - a) * t;
|
||||
}
|
||||
}
|
||||
120
backend/HMIWeb/MainForm.resx
Normal file
120
backend/HMIWeb/MainForm.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
22
backend/HMIWeb/Program.cs
Normal file
22
backend/HMIWeb/Program.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace HMIWeb
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 해당 애플리케이션의 주 진입점입니다.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
}
|
||||
}
|
||||
}
|
||||
33
backend/HMIWeb/Properties/AssemblyInfo.cs
Normal file
33
backend/HMIWeb/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해
|
||||
// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면
|
||||
// 이러한 특성 값을 변경하세요.
|
||||
[assembly: AssemblyTitle("HMIWeb")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("HMIWeb")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에
|
||||
// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면
|
||||
// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다.
|
||||
[assembly: Guid("33bfbc63-d007-4922-8412-99776b42a016")]
|
||||
|
||||
// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다.
|
||||
//
|
||||
// 주 버전
|
||||
// 부 버전
|
||||
// 빌드 번호
|
||||
// 수정 버전
|
||||
//
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
71
backend/HMIWeb/Properties/Resources.Designer.cs
generated
Normal file
71
backend/HMIWeb/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,71 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
||||
// 런타임 버전:4.0.30319.42000
|
||||
//
|
||||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
||||
// 이러한 변경 내용이 손실됩니다.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace HMIWeb.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
|
||||
/// </summary>
|
||||
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
|
||||
// 클래스에서 자동으로 생성되었습니다.
|
||||
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여
|
||||
// ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HMIWeb.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 현재 스레드의 CurrentUICulture 속성을
|
||||
/// 재정의합니다.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
backend/HMIWeb/Properties/Resources.resx
Normal file
117
backend/HMIWeb/Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
30
backend/HMIWeb/Properties/Settings.Designer.cs
generated
Normal file
30
backend/HMIWeb/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace HMIWeb.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
backend/HMIWeb/Properties/Settings.settings
Normal file
7
backend/HMIWeb/Properties/Settings.settings
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
222
backend/HMIWeb/WebSocketServer.cs
Normal file
222
backend/HMIWeb/WebSocketServer.cs
Normal file
@@ -0,0 +1,222 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace HMIWeb
|
||||
{
|
||||
public class WebSocketServer
|
||||
{
|
||||
private HttpListener _httpListener;
|
||||
private List<WebSocket> _clients = new List<WebSocket>();
|
||||
private MainForm _mainForm;
|
||||
|
||||
public WebSocketServer(string url, MainForm form)
|
||||
{
|
||||
_mainForm = form;
|
||||
_httpListener = new HttpListener();
|
||||
_httpListener.Prefixes.Add(url);
|
||||
_httpListener.Start();
|
||||
Console.WriteLine($"[WS] Listening on {url}");
|
||||
Task.Run(AcceptConnections);
|
||||
}
|
||||
|
||||
private async Task AcceptConnections()
|
||||
{
|
||||
while (_httpListener.IsListening)
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = await _httpListener.GetContextAsync();
|
||||
if (context.Request.IsWebSocketRequest)
|
||||
{
|
||||
ProcessRequest(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[WS] Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.Concurrent.ConcurrentDictionary<WebSocket, SemaphoreSlim> _socketLocks = new System.Collections.Concurrent.ConcurrentDictionary<WebSocket, SemaphoreSlim>();
|
||||
|
||||
private async void ProcessRequest(HttpListenerContext context)
|
||||
{
|
||||
WebSocketContext wsContext = null;
|
||||
try
|
||||
{
|
||||
wsContext = await context.AcceptWebSocketAsync(subProtocol: null);
|
||||
WebSocket socket = wsContext.WebSocket;
|
||||
_socketLocks.TryAdd(socket, new SemaphoreSlim(1, 1));
|
||||
|
||||
lock (_clients) { _clients.Add(socket); }
|
||||
Console.WriteLine("[WS] Client Connected");
|
||||
|
||||
await ReceiveLoop(socket);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[WS] Accept Error: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (wsContext != null)
|
||||
{
|
||||
WebSocket socket = wsContext.WebSocket;
|
||||
lock (_clients) { _clients.Remove(socket); }
|
||||
|
||||
if (_socketLocks.TryRemove(socket, out var semaphore))
|
||||
{
|
||||
semaphore.Dispose();
|
||||
}
|
||||
socket.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveLoop(WebSocket socket)
|
||||
{
|
||||
var buffer = new byte[1024 * 4];
|
||||
while (socket.State == WebSocketState.Open)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
||||
}
|
||||
else if (result.MessageType == WebSocketMessageType.Text)
|
||||
{
|
||||
string msg = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
HandleMessage(msg, socket);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void HandleMessage(string msg, WebSocket socket)
|
||||
{
|
||||
// Simple JSON parsing (manual or Newtonsoft)
|
||||
// Expected format: { "type": "...", "data": ... }
|
||||
try
|
||||
{
|
||||
dynamic json = Newtonsoft.Json.JsonConvert.DeserializeObject(msg);
|
||||
string type = json.type;
|
||||
|
||||
if (type == "GET_CONFIG")
|
||||
{
|
||||
// Simulate Delay for Loading Screen Test
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Send Config back
|
||||
var bridge = new MachineBridge(_mainForm); // Re-use logic
|
||||
string configJson = bridge.GetConfig();
|
||||
var response = new { type = "CONFIG_DATA", data = Newtonsoft.Json.JsonConvert.DeserializeObject(configJson) };
|
||||
await Send(socket, Newtonsoft.Json.JsonConvert.SerializeObject(response));
|
||||
}
|
||||
else if (type == "SAVE_CONFIG")
|
||||
{
|
||||
string configJson = Newtonsoft.Json.JsonConvert.SerializeObject(json.data);
|
||||
var bridge = new MachineBridge(_mainForm);
|
||||
bridge.SaveConfig(configJson);
|
||||
}
|
||||
else if (type == "CONTROL")
|
||||
{
|
||||
string cmd = json.command;
|
||||
_mainForm.Invoke(new Action(() => _mainForm.HandleCommand(cmd)));
|
||||
}
|
||||
else if (type == "MOVE")
|
||||
{
|
||||
string axis = json.axis;
|
||||
double val = json.value;
|
||||
_mainForm.Invoke(new Action(() => _mainForm.SetTargetPosition(axis, val)));
|
||||
}
|
||||
else if (type == "SET_IO")
|
||||
{
|
||||
int id = json.id;
|
||||
bool state = json.state;
|
||||
_mainForm.Invoke(new Action(() => _mainForm.SetOutput(id, state)));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[WS] Msg Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Send(WebSocket socket, string message)
|
||||
{
|
||||
if (_socketLocks.TryGetValue(socket, out var semaphore))
|
||||
{
|
||||
await semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (socket.State == WebSocketState.Open)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
||||
await socket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void Broadcast(string message)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
||||
WebSocket[] clientsCopy;
|
||||
|
||||
lock (_clients)
|
||||
{
|
||||
clientsCopy = _clients.ToArray();
|
||||
}
|
||||
|
||||
foreach (var client in clientsCopy)
|
||||
{
|
||||
if (client.State == WebSocketState.Open && _socketLocks.TryGetValue(client, out var semaphore))
|
||||
{
|
||||
// Fire and forget, but safely
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
// Try to get lock immediately. If busy (sending previous frame), skip this frame to prevent lag.
|
||||
if (await semaphore.WaitAsync(0))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (client.State == WebSocketState.Open)
|
||||
{
|
||||
await client.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
catch { /* Ignore send errors */ }
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
backend/HMIWeb/packages.config
Normal file
5
backend/HMIWeb/packages.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.3595.46" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net48" />
|
||||
</packages>
|
||||
10
backend/build.bat
Normal file
10
backend/build.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
echo Building Backend...
|
||||
"C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" "HMIWeb.sln" /t:Build /p:Configuration=Debug
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo Build Failed!
|
||||
pause
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
echo Build Success!
|
||||
pause
|
||||
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/.signature.p7s
vendored
Normal file
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/.signature.p7s
vendored
Normal file
Binary file not shown.
27
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/LICENSE.txt
vendored
Normal file
27
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* The name of Microsoft Corporation, or the names of its contributors
|
||||
may not be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/Microsoft.Web.WebView2.1.0.3595.46.nupkg
vendored
Normal file
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/Microsoft.Web.WebView2.1.0.3595.46.nupkg
vendored
Normal file
Binary file not shown.
85
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/NOTICE.txt
vendored
Normal file
85
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/NOTICE.txt
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
NOTICES AND INFORMATION
|
||||
Do Not Translate or Localize
|
||||
|
||||
This software incorporates material from third parties. Microsoft makes certain
|
||||
open source code available at https://3rdpartysource.microsoft.com, or you may
|
||||
send a check or money order for US $5.00, including the product name, the open
|
||||
source component name, and version number, to:
|
||||
|
||||
Source Code Compliance Team
|
||||
Microsoft Corporation
|
||||
One Microsoft Way
|
||||
Redmond, WA 98052
|
||||
USA
|
||||
|
||||
Notwithstanding any other terms, you may reverse engineer this software to the
|
||||
extent required to debug changes to any libraries licensed under the GNU Lesser
|
||||
General Public License.
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
Antlr3.Runtime 3.5.2-rc1 - BSD 3-Clause
|
||||
|
||||
[The "BSD license"]
|
||||
Copyright (c) 2011 The ANTLR Project
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
---------------------------------------------------------
|
||||
|
||||
StringTemplate4 4.0.9-rc1 - BSD 3-Clause
|
||||
|
||||
[The "BSD license"]
|
||||
Copyright (c) 2011 The ANTLR Project
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---------------------------------------------------------
|
||||
11100
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/WebView2.idl
vendored
Normal file
11100
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/WebView2.idl
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/WebView2.tlb
vendored
Normal file
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/WebView2.tlb
vendored
Normal file
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- This build/.targets file is used by managed VS projects including win32
|
||||
apps and UWP apps. -->
|
||||
|
||||
<PropertyGroup>
|
||||
<WebView2ProjectKind>managed</WebView2ProjectKind>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- The managed targets file is under build
|
||||
so the root is one path segment up. -->
|
||||
<NugetRoot>$(MSBuildThisFileDirectory)..\</NugetRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Example logging
|
||||
<Target Name="WebView2ManagedEntryLog" BeforeTargets="Build">
|
||||
<Message Text="WebView2 managed .targets file. $(NugetRoot)" Importance="high"/>
|
||||
</Target>
|
||||
-->
|
||||
|
||||
<Import Project="$(NugetRoot)\build\Common.targets"/>
|
||||
</Project>
|
||||
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/Microsoft.Web.WebView2.Core.winmd
vendored
Normal file
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/Microsoft.Web.WebView2.Core.winmd
vendored
Normal file
Binary file not shown.
6817
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/net462/Microsoft.Web.WebView2.Core.xml
vendored
Normal file
6817
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/net462/Microsoft.Web.WebView2.Core.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
510
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/net462/Microsoft.Web.WebView2.WinForms.xml
vendored
Normal file
510
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/net462/Microsoft.Web.WebView2.WinForms.xml
vendored
Normal file
@@ -0,0 +1,510 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>Microsoft.Web.WebView2.WinForms</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties">
|
||||
<summary>
|
||||
This class is a bundle of the most common parameters used to create <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> and <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/> instances.
|
||||
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> in order to customize the environment and/or controller used by a <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> during implicit initialization.
|
||||
</summary>
|
||||
<remarks>
|
||||
This class isn't intended to contain all possible environment or controller customization options.
|
||||
If you need complete control over the environment and/or controller used by a WebView2 control then you'll need to initialize the control explicitly by
|
||||
creating your own environment (with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/>) and/or controller options (with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateCoreWebView2ControllerOptions"/>) and passing them to <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/>
|
||||
*before* you set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property to anything.
|
||||
See the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> class documentation for an initialization overview.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.#ctor">
|
||||
<summary>
|
||||
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties"/> with default data for all properties.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.BrowserExecutableFolder">
|
||||
<summary>
|
||||
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.UserDataFolder">
|
||||
<summary>
|
||||
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.Language">
|
||||
<summary>
|
||||
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.ProfileName">
|
||||
<summary>
|
||||
Gets or sets the value to use for the ProfileName property of the CoreWebView2ControllerOptions parameter passed to CreateCoreWebView2ControllerWithOptionsAsync when creating an controller with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.AdditionalBrowserArguments">
|
||||
<summary>
|
||||
Gets or sets the value to pass as the AdditionalBrowserArguments parameter of <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions"/> which is passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.IsInPrivateModeEnabled">
|
||||
<summary>
|
||||
Gets or sets the value to use for the IsInPrivateModeEnabled property of the CoreWebView2ControllerOptions parameter passed to CreateCoreWebView2ControllerWithOptionsAsync when creating an controller with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateEnvironmentAsync">
|
||||
<summary>
|
||||
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
|
||||
</summary>
|
||||
<returns>A task which will provide the created environment on completion, or null if no environment-related options are set.</returns>
|
||||
<remarks>
|
||||
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
|
||||
If some other property is changed then the next call to this method will return a different task/environment.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateCoreWebView2ControllerOptions(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
|
||||
<summary>
|
||||
Creates a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> using the current values of this instance's properties.
|
||||
</summary>
|
||||
<returns>A <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> object or null if no controller-related properties are set.</returns>
|
||||
<exception cref="T:System.NullReferenceException">Thrown if the parameter environment is null.</exception>
|
||||
</member>
|
||||
<member name="T:Microsoft.Web.WebView2.WinForms.WebView2">
|
||||
<summary>
|
||||
Control to embed WebView2 in WinForms.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.#ctor">
|
||||
<summary>
|
||||
Create a new WebView2 WinForms control.
|
||||
After construction the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property is <c>null</c>.
|
||||
Call <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> to initialize the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
|
||||
</summary>
|
||||
<remarks>
|
||||
This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2
|
||||
You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property.
|
||||
Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control.
|
||||
|
||||
Upon creation, the control's CoreWebView2 property will be null.
|
||||
This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes.
|
||||
There are two ways to cause the CoreWebView2 to be created:
|
||||
1) Call the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> method. This is referred to as explicit initialization.
|
||||
2) Set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property. This is referred to as implicit initialization.
|
||||
Either option will start initialization in the background and return back to the caller without waiting for it to finish.
|
||||
To specify options regarding the initialization process, either pass your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to EnsureCoreWebView2Async or set the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property prior to initialization.
|
||||
|
||||
When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order:
|
||||
1) The control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event.
|
||||
2) If a Uri has been set to the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish).
|
||||
3) The Task returned from <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> will complete.
|
||||
|
||||
For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation.
|
||||
|
||||
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
|
||||
fire standard key press events such as OnKeyDown. You can suppress the
|
||||
control's default implementation of an accelerator key press (e.g.
|
||||
printing, in the case of Ctrl+P) by setting the Handled property of its
|
||||
EventArgs to true. Also note that the underlying browser process is
|
||||
blocked while these handlers execute, so:
|
||||
<list type="number">
|
||||
<item>
|
||||
You should avoid doing a lot of work in these handlers.
|
||||
</item>
|
||||
<item>
|
||||
Some of the WebView2 and CoreWebView2 APIs may throw errors if
|
||||
invoked within these handlers due to being unable to communicate with
|
||||
the browser process.
|
||||
</item>
|
||||
</list>
|
||||
If you need to do a lot of work and/or invoke WebView2 APIs in response to
|
||||
accelerator keys then consider kicking off a background task or queuing
|
||||
the work for later execution on the UI thread.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)">
|
||||
<summary>
|
||||
Cleans up any resources being used.
|
||||
</summary>
|
||||
<param name="disposing"><c>true</c> if managed resources should be disposed; otherwise, <c>false</c>.</param>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnPaint(System.Windows.Forms.PaintEventArgs)">
|
||||
<summary>
|
||||
Overrides the base OnPaint event to have custom actions
|
||||
in designer mode
|
||||
</summary>
|
||||
<param name="e">The graphics devices which is the source</param>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.WndProc(System.Windows.Forms.Message@)">
|
||||
<summary>
|
||||
Overrides the base WndProc events to handle specific window messages.
|
||||
</summary>
|
||||
<param name="m">The Message object containing the HWND window message and parameters</param>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties">
|
||||
<summary>
|
||||
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown if initialization of the control's CoreWebView2 has already started.</exception>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)">
|
||||
<summary>
|
||||
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
</summary>
|
||||
<param name="environment">
|
||||
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
|
||||
If you pass <c>null</c> (the default value) then a default environment will be created and used automatically.
|
||||
</param>
|
||||
<param name="controllerOptions">
|
||||
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> that should be used to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
|
||||
Creating your own controller options gives you control over several options that affect how the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> is initialized.
|
||||
If you pass a controllerOptions to this method then it will override any settings specified on the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property.
|
||||
If you pass <c>null</c> (the default value) and no value has been set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> then a default controllerOptions will be created and used automatically.
|
||||
</param>
|
||||
<returns>
|
||||
A Task that represents the background initialization process.
|
||||
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
|
||||
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
|
||||
or on exceptions.
|
||||
</returns>
|
||||
<remarks>
|
||||
Unless previous initialization has already failed, calling this method additional times with the same parameter will have no effect (any specified environment is ignored) and return the same Task as the first call.
|
||||
Unless previous initialization has already failed, calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect if no environment is given
|
||||
and simply return a Task representing that initialization already in progress.
|
||||
Unless previous initialization has already failed, calling this method with a different environment after initialization has begun will result in an <see cref="T:System.ArgumentException"/>. For example, this can happen if you begin initialization
|
||||
by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property and then call this method with a new environment, if you begin initialization with <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> and then call this method with a new
|
||||
environment, or if you begin initialization with one environment and then call this method with no environment specified.
|
||||
When this method is called after previous initialization has failed, it will trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> again.
|
||||
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
|
||||
<para>
|
||||
The following summarizes the possible error values and a description of why these errors occur.
|
||||
<list type="table">
|
||||
<listheader>
|
||||
<description>Error Value</description>
|
||||
<description>Description</description>
|
||||
</listheader>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)</c></description>
|
||||
<description>*\\Edge\\Application* path used in browserExecutableFolder.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_INVALID_STATE)</c></description>
|
||||
<description>Specified options do not match the options of the WebViews that are currently running in the shared browser process.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_INVALID_WINDOW_HANDLE)</c></description>
|
||||
<description>WebView2 Initialization failed due to an invalid host HWND parentWindow.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_DISK_FULL)</c></description>
|
||||
<description>WebView2 Initialization failed due to reaching the maximum number of installed runtime versions.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_PRODUCT_UNINSTALLED</c></description>
|
||||
<description>If the Webview depends upon an installed WebView2 Runtime version and it is uninstalled.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)</c></description>
|
||||
<description>Could not find Edge installation.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_FILE_EXISTS)</c></description>
|
||||
<description>User data folder cannot be created because a file with the same name already exists.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>E_ACCESSDENIED</c></description>
|
||||
<description>Unable to create user data folder, Access Denied.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>E_FAIL</c></description>
|
||||
<description>Edge runtime unable to start.</description>
|
||||
</item>
|
||||
</list>
|
||||
</para>
|
||||
</remarks>
|
||||
<exception cref="T:System.ArgumentException">
|
||||
Thrown if this method is called with a different environment than when it was initialized. See Remarks for more info.
|
||||
</exception>
|
||||
<exception cref="T:System.InvalidOperationException">
|
||||
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
|
||||
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
|
||||
</exception>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
|
||||
<summary>
|
||||
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
</summary>
|
||||
<param name="environment">
|
||||
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
|
||||
If you pass <c>null</c> then a default environment will be created and used automatically.
|
||||
</param>
|
||||
<returns>
|
||||
A Task that represents the background initialization process.
|
||||
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
|
||||
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
|
||||
or on exceptions.
|
||||
</returns>
|
||||
<remarks>
|
||||
Unless previous initialization has already failed, calling this method additional times with the same parameter will have no effect (any specified environment is ignored) and return the same Task as the first call.
|
||||
Unless previous initialization has already failed, calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect if no environment is given
|
||||
and simply return a Task representing that initialization already in progress.
|
||||
Unless previous initialization has already failed, calling this method with a different environment after initialization has begun will result in an <see cref="T:System.ArgumentException"/>. For example, this can happen if you begin initialization
|
||||
by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property and then call this method with a new environment, if you begin initialization with <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> and then call this method with a new
|
||||
environment, or if you begin initialization with one environment and then call this method with no environment specified.
|
||||
When this method is called after previous initialization has failed, it will trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> again.
|
||||
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
|
||||
</remarks>
|
||||
<exception cref="T:System.ArgumentException">
|
||||
Thrown if this method is called with a different environment than when it was initialized. See Remarks for more info.
|
||||
</exception>
|
||||
<exception cref="T:System.InvalidOperationException">
|
||||
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
|
||||
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
|
||||
</exception>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)">
|
||||
<summary>
|
||||
This is the private function which implements the actual background initialization task.
|
||||
Cannot be called if the control is already initialized or has been disposed.
|
||||
</summary>
|
||||
<param name="environment">
|
||||
The environment to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
|
||||
If that is null then a default environment is created with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and its default parameters.
|
||||
</param>
|
||||
<param name="controllerOptions">
|
||||
The controllerOptions to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
|
||||
If that is null then a default controllerOptions is created with its default parameters.
|
||||
</param>
|
||||
<returns>A task representing the background initialization process.</returns>
|
||||
<remarks>All the event handlers added here need to be removed in <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)"/>.</remarks>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreateParams">
|
||||
<summary>
|
||||
Protected CreateParams property. Used to set custom window styles to the forms HWND.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnVisibleChanged(System.EventArgs)">
|
||||
<summary>
|
||||
Protected VisibilityChanged handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnSizeChanged(System.EventArgs)">
|
||||
<summary>
|
||||
Protected SizeChanged handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Select(System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
Protected Select method: override this to capture tab direction when WebView control is activated
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ProcessDialogKey(System.Windows.Forms.Keys)">
|
||||
<summary>Processes a dialog key.</summary>
|
||||
<param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys" /> values that represents the key to process.</param>
|
||||
<returns>
|
||||
<see langword="true" /> if the key was processed by the control; otherwise, <see langword="false" />.</returns>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnGotFocus(System.EventArgs)">
|
||||
<summary>
|
||||
Protected OnGotFocus handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnParentChanged(System.EventArgs)">
|
||||
<summary>
|
||||
Protected OnParentChanged handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.IsInitialized">
|
||||
<summary>
|
||||
True if initialization finished successfully and the control is not disposed yet.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GetSitedParentSite(System.Windows.Forms.Control)">
|
||||
<summary>
|
||||
Recursive retrieval of the parent control
|
||||
</summary>
|
||||
<param name="control">The control to get the parent for</param>
|
||||
<returns>The root parent control</returns>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2">
|
||||
<summary>
|
||||
The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed
|
||||
on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed.
|
||||
You can force the underlying CoreWebView2 to
|
||||
initialize via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> method.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.</exception>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor">
|
||||
<summary>
|
||||
The zoom factor for the WebView.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.AllowExternalDrop">
|
||||
<summary>
|
||||
Enable/disable external drop.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.Source">
|
||||
<summary>
|
||||
The Source property is the URI of the top level document of the
|
||||
WebView2. Setting the Source is equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>.
|
||||
Setting the Source will trigger initialization of the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>, if not already initialized.
|
||||
The default value of Source is <c>null</c>, indicating that the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.
|
||||
</summary>
|
||||
<exception cref="T:System.ArgumentException">Specified value is not an absolute <see cref="T:System.Uri"/>.</exception>
|
||||
<exception cref="T:System.NotImplementedException">Specified value is <c>null</c> and the control is initialized.</exception>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoForward">
|
||||
<summary>
|
||||
Returns true if the webview can navigate to a next page in the
|
||||
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward"/> method.
|
||||
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
|
||||
</summary>
|
||||
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoBack">
|
||||
<summary>
|
||||
Returns <c>true</c> if the webview can navigate to a previous page in the
|
||||
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack"/> method.
|
||||
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
|
||||
</summary>
|
||||
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.DefaultBackgroundColor">
|
||||
<summary>
|
||||
The default background color for the WebView.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ExecuteScriptAsync(System.String)">
|
||||
<summary>
|
||||
Executes the provided script in the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Reload">
|
||||
<summary>
|
||||
Reloads the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward">
|
||||
<summary>
|
||||
Navigates to the next page in navigation history.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
|
||||
</summary>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack">
|
||||
<summary>
|
||||
Navigates to the previous page in navigation history.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
|
||||
</summary>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.NavigateToString(System.String)">
|
||||
<summary>
|
||||
Renders the provided HTML as the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
|
||||
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Stop">
|
||||
<summary>
|
||||
Stops any in progress navigation in the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
|
||||
</summary>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted">
|
||||
<summary>
|
||||
This event is triggered either 1) when the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything
|
||||
OR 2) the initialization failed.
|
||||
You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages
|
||||
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
|
||||
</summary>
|
||||
<remarks>
|
||||
This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time
|
||||
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is true.
|
||||
Unlikely this event can fire second time (after reporting initialization success first)
|
||||
if the initialization is followed by navigation which fails.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationStarting">
|
||||
<summary>
|
||||
NavigationStarting dispatches before a new navigate starts for the top
|
||||
level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationCompleted">
|
||||
<summary>
|
||||
NavigationCompleted dispatches after a navigate of the top level
|
||||
document completes rendering either successfully or not.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.WebMessageReceived">
|
||||
<summary>
|
||||
WebMessageReceived dispatches after web content sends a message to the
|
||||
app host via <c>chrome.webview.postMessage</c>.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.SourceChanged">
|
||||
<summary>
|
||||
SourceChanged dispatches after the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property changes. This may happen
|
||||
during a navigation or if otherwise the script in the page changes the
|
||||
URI of the document.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ContentLoading">
|
||||
<summary>
|
||||
ContentLoading dispatches after a navigation begins to a new URI and the
|
||||
content of that URI begins to render.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactorChanged">
|
||||
<summary>
|
||||
ZoomFactorChanged dispatches when the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor"/> property changes.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
|
||||
</member>
|
||||
<member name="F:Microsoft.Web.WebView2.WinForms.WebView2.components">
|
||||
<summary>
|
||||
Required designer variable.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitializeComponent">
|
||||
<summary>
|
||||
Required method for Designer support - do not modify
|
||||
the contents of this method with the code editor.
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
1902
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/net462/Microsoft.Web.WebView2.Wpf.xml
vendored
Normal file
1902
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/lib/net462/Microsoft.Web.WebView2.Wpf.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,510 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>Microsoft.Web.WebView2.WinForms</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties">
|
||||
<summary>
|
||||
This class is a bundle of the most common parameters used to create <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> and <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/> instances.
|
||||
Its main purpose is to be set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> in order to customize the environment and/or controller used by a <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> during implicit initialization.
|
||||
</summary>
|
||||
<remarks>
|
||||
This class isn't intended to contain all possible environment or controller customization options.
|
||||
If you need complete control over the environment and/or controller used by a WebView2 control then you'll need to initialize the control explicitly by
|
||||
creating your own environment (with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/>) and/or controller options (with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateCoreWebView2ControllerOptions"/>) and passing them to <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/>
|
||||
*before* you set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property to anything.
|
||||
See the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/> class documentation for an initialization overview.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.#ctor">
|
||||
<summary>
|
||||
Creates a new instance of <see cref="T:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties"/> with default data for all properties.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.BrowserExecutableFolder">
|
||||
<summary>
|
||||
Gets or sets the value to pass as the browserExecutableFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.UserDataFolder">
|
||||
<summary>
|
||||
Gets or sets the value to pass as the userDataFolder parameter of <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.Language">
|
||||
<summary>
|
||||
Gets or sets the value to use for the Language property of the CoreWebView2EnvironmentOptions parameter passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.ProfileName">
|
||||
<summary>
|
||||
Gets or sets the value to use for the ProfileName property of the CoreWebView2ControllerOptions parameter passed to CreateCoreWebView2ControllerWithOptionsAsync when creating an controller with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.AdditionalBrowserArguments">
|
||||
<summary>
|
||||
Gets or sets the value to pass as the AdditionalBrowserArguments parameter of <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions"/> which is passed to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> when creating an environment with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.IsInPrivateModeEnabled">
|
||||
<summary>
|
||||
Gets or sets the value to use for the IsInPrivateModeEnabled property of the CoreWebView2ControllerOptions parameter passed to CreateCoreWebView2ControllerWithOptionsAsync when creating an controller with this instance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateEnvironmentAsync">
|
||||
<summary>
|
||||
Create a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> using the current values of this instance's properties.
|
||||
</summary>
|
||||
<returns>A task which will provide the created environment on completion, or null if no environment-related options are set.</returns>
|
||||
<remarks>
|
||||
As long as no other properties on this instance are changed, repeated calls to this method will return the same task/environment as earlier calls.
|
||||
If some other property is changed then the next call to this method will return a different task/environment.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties.CreateCoreWebView2ControllerOptions(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
|
||||
<summary>
|
||||
Creates a <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> using the current values of this instance's properties.
|
||||
</summary>
|
||||
<returns>A <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> object or null if no controller-related properties are set.</returns>
|
||||
<exception cref="T:System.NullReferenceException">Thrown if the parameter environment is null.</exception>
|
||||
</member>
|
||||
<member name="T:Microsoft.Web.WebView2.WinForms.WebView2">
|
||||
<summary>
|
||||
Control to embed WebView2 in WinForms.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.#ctor">
|
||||
<summary>
|
||||
Create a new WebView2 WinForms control.
|
||||
After construction the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property is <c>null</c>.
|
||||
Call <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> to initialize the underlying <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
|
||||
</summary>
|
||||
<remarks>
|
||||
This control is effectively a wrapper around the WebView2 COM API, which you can find documentation for here: https://aka.ms/webview2
|
||||
You can directly access the underlying ICoreWebView2 interface and all of its functionality by accessing the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property.
|
||||
Some of the most common COM functionality is also accessible directly through wrapper methods/properties/events on the control.
|
||||
|
||||
Upon creation, the control's CoreWebView2 property will be null.
|
||||
This is because creating the CoreWebView2 is an expensive operation which involves things like launching Edge browser processes.
|
||||
There are two ways to cause the CoreWebView2 to be created:
|
||||
1) Call the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> method. This is referred to as explicit initialization.
|
||||
2) Set the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property. This is referred to as implicit initialization.
|
||||
Either option will start initialization in the background and return back to the caller without waiting for it to finish.
|
||||
To specify options regarding the initialization process, either pass your own <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> to EnsureCoreWebView2Async or set the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property prior to initialization.
|
||||
|
||||
When initialization has finished (regardless of how it was triggered) then the following things will occur, in this order:
|
||||
1) The control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked. If you need to perform one time setup operations on the CoreWebView2 prior to its use then you should do so in a handler for that event.
|
||||
2) If a Uri has been set to the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property then the control will start navigating to it in the background (i.e. these steps will continue without waiting for the navigation to finish).
|
||||
3) The Task returned from <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> will complete.
|
||||
|
||||
For more details about any of the methods/properties/events involved in the initialization process, see its specific documentation.
|
||||
|
||||
Accelerator key presses (e.g. Ctrl+P) that occur within the control will
|
||||
fire standard key press events such as OnKeyDown. You can suppress the
|
||||
control's default implementation of an accelerator key press (e.g.
|
||||
printing, in the case of Ctrl+P) by setting the Handled property of its
|
||||
EventArgs to true. Also note that the underlying browser process is
|
||||
blocked while these handlers execute, so:
|
||||
<list type="number">
|
||||
<item>
|
||||
You should avoid doing a lot of work in these handlers.
|
||||
</item>
|
||||
<item>
|
||||
Some of the WebView2 and CoreWebView2 APIs may throw errors if
|
||||
invoked within these handlers due to being unable to communicate with
|
||||
the browser process.
|
||||
</item>
|
||||
</list>
|
||||
If you need to do a lot of work and/or invoke WebView2 APIs in response to
|
||||
accelerator keys then consider kicking off a background task or queuing
|
||||
the work for later execution on the UI thread.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)">
|
||||
<summary>
|
||||
Cleans up any resources being used.
|
||||
</summary>
|
||||
<param name="disposing"><c>true</c> if managed resources should be disposed; otherwise, <c>false</c>.</param>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnPaint(System.Windows.Forms.PaintEventArgs)">
|
||||
<summary>
|
||||
Overrides the base OnPaint event to have custom actions
|
||||
in designer mode
|
||||
</summary>
|
||||
<param name="e">The graphics devices which is the source</param>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.WndProc(System.Windows.Forms.Message@)">
|
||||
<summary>
|
||||
Overrides the base WndProc events to handle specific window messages.
|
||||
</summary>
|
||||
<param name="m">The Message object containing the HWND window message and parameters</param>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties">
|
||||
<summary>
|
||||
Gets or sets a bag of options which are used during initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
This property cannot be modified (an exception will be thrown) after initialization of the control's CoreWebView2 has started.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown if initialization of the control's CoreWebView2 has already started.</exception>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)">
|
||||
<summary>
|
||||
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
</summary>
|
||||
<param name="environment">
|
||||
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
|
||||
If you pass <c>null</c> (the default value) then a default environment will be created and used automatically.
|
||||
</param>
|
||||
<param name="controllerOptions">
|
||||
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions"/> that should be used to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/>.
|
||||
Creating your own controller options gives you control over several options that affect how the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2"/> is initialized.
|
||||
If you pass a controllerOptions to this method then it will override any settings specified on the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> property.
|
||||
If you pass <c>null</c> (the default value) and no value has been set to <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> then a default controllerOptions will be created and used automatically.
|
||||
</param>
|
||||
<returns>
|
||||
A Task that represents the background initialization process.
|
||||
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
|
||||
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
|
||||
or on exceptions.
|
||||
</returns>
|
||||
<remarks>
|
||||
Unless previous initialization has already failed, calling this method additional times with the same parameter will have no effect (any specified environment is ignored) and return the same Task as the first call.
|
||||
Unless previous initialization has already failed, calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect if no environment is given
|
||||
and simply return a Task representing that initialization already in progress.
|
||||
Unless previous initialization has already failed, calling this method with a different environment after initialization has begun will result in an <see cref="T:System.ArgumentException"/>. For example, this can happen if you begin initialization
|
||||
by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property and then call this method with a new environment, if you begin initialization with <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> and then call this method with a new
|
||||
environment, or if you begin initialization with one environment and then call this method with no environment specified.
|
||||
When this method is called after previous initialization has failed, it will trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> again.
|
||||
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
|
||||
<para>
|
||||
The following summarizes the possible error values and a description of why these errors occur.
|
||||
<list type="table">
|
||||
<listheader>
|
||||
<description>Error Value</description>
|
||||
<description>Description</description>
|
||||
</listheader>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)</c></description>
|
||||
<description>*\\Edge\\Application* path used in browserExecutableFolder.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_INVALID_STATE)</c></description>
|
||||
<description>Specified options do not match the options of the WebViews that are currently running in the shared browser process.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_INVALID_WINDOW_HANDLE)</c></description>
|
||||
<description>WebView2 Initialization failed due to an invalid host HWND parentWindow.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_DISK_FULL)</c></description>
|
||||
<description>WebView2 Initialization failed due to reaching the maximum number of installed runtime versions.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_PRODUCT_UNINSTALLED</c></description>
|
||||
<description>If the Webview depends upon an installed WebView2 Runtime version and it is uninstalled.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)</c></description>
|
||||
<description>Could not find Edge installation.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>HRESULT_FROM_WIN32(ERROR_FILE_EXISTS)</c></description>
|
||||
<description>User data folder cannot be created because a file with the same name already exists.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>E_ACCESSDENIED</c></description>
|
||||
<description>Unable to create user data folder, Access Denied.</description>
|
||||
</item>
|
||||
<item>
|
||||
<description><c>E_FAIL</c></description>
|
||||
<description>Edge runtime unable to start.</description>
|
||||
</item>
|
||||
</list>
|
||||
</para>
|
||||
</remarks>
|
||||
<exception cref="T:System.ArgumentException">
|
||||
Thrown if this method is called with a different environment than when it was initialized. See Remarks for more info.
|
||||
</exception>
|
||||
<exception cref="T:System.InvalidOperationException">
|
||||
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
|
||||
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
|
||||
</exception>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment)">
|
||||
<summary>
|
||||
Explicitly trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
</summary>
|
||||
<param name="environment">
|
||||
A pre-created <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Environment"/> that should be used to create the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>.
|
||||
Creating your own environment gives you control over several options that affect how the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is initialized.
|
||||
If you pass <c>null</c> then a default environment will be created and used automatically.
|
||||
</param>
|
||||
<returns>
|
||||
A Task that represents the background initialization process.
|
||||
When the task completes then the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> property will be available for use (i.e. non-null).
|
||||
Note that the control's <see cref="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted"/> event will be invoked before the task completes
|
||||
or on exceptions.
|
||||
</returns>
|
||||
<remarks>
|
||||
Unless previous initialization has already failed, calling this method additional times with the same parameter will have no effect (any specified environment is ignored) and return the same Task as the first call.
|
||||
Unless previous initialization has already failed, calling this method after initialization has been implicitly triggered by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property will have no effect if no environment is given
|
||||
and simply return a Task representing that initialization already in progress.
|
||||
Unless previous initialization has already failed, calling this method with a different environment after initialization has begun will result in an <see cref="T:System.ArgumentException"/>. For example, this can happen if you begin initialization
|
||||
by setting the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property and then call this method with a new environment, if you begin initialization with <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CreationProperties"/> and then call this method with a new
|
||||
environment, or if you begin initialization with one environment and then call this method with no environment specified.
|
||||
When this method is called after previous initialization has failed, it will trigger initialization of the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> again.
|
||||
Note that even though this method is asynchronous and returns a Task, it still must be called on the UI thread like most public functionality of most UI controls.
|
||||
</remarks>
|
||||
<exception cref="T:System.ArgumentException">
|
||||
Thrown if this method is called with a different environment than when it was initialized. See Remarks for more info.
|
||||
</exception>
|
||||
<exception cref="T:System.InvalidOperationException">
|
||||
Thrown if this instance of <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is already disposed, or if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.
|
||||
May also be thrown if the browser process has crashed unexpectedly and left the control in an invalid state. We are considering throwing a different type of exception for this case in the future.
|
||||
</exception>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)">
|
||||
<summary>
|
||||
This is the private function which implements the actual background initialization task.
|
||||
Cannot be called if the control is already initialized or has been disposed.
|
||||
</summary>
|
||||
<param name="environment">
|
||||
The environment to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
|
||||
If that is null then a default environment is created with <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2Environment.CreateAsync(System.String,System.String,Microsoft.Web.WebView2.Core.CoreWebView2EnvironmentOptions)"/> and its default parameters.
|
||||
</param>
|
||||
<param name="controllerOptions">
|
||||
The controllerOptions to use to create the <see cref="T:Microsoft.Web.WebView2.Core.CoreWebView2Controller"/>.
|
||||
If that is null then a default controllerOptions is created with its default parameters.
|
||||
</param>
|
||||
<returns>A task representing the background initialization process.</returns>
|
||||
<remarks>All the event handlers added here need to be removed in <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.Dispose(System.Boolean)"/>.</remarks>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CreateParams">
|
||||
<summary>
|
||||
Protected CreateParams property. Used to set custom window styles to the forms HWND.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnVisibleChanged(System.EventArgs)">
|
||||
<summary>
|
||||
Protected VisibilityChanged handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnSizeChanged(System.EventArgs)">
|
||||
<summary>
|
||||
Protected SizeChanged handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Select(System.Boolean,System.Boolean)">
|
||||
<summary>
|
||||
Protected Select method: override this to capture tab direction when WebView control is activated
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ProcessDialogKey(System.Windows.Forms.Keys)">
|
||||
<summary>Processes a dialog key.</summary>
|
||||
<param name="keyData">One of the <see cref="T:System.Windows.Forms.Keys" /> values that represents the key to process.</param>
|
||||
<returns>
|
||||
<see langword="true" /> if the key was processed by the control; otherwise, <see langword="false" />.</returns>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnGotFocus(System.EventArgs)">
|
||||
<summary>
|
||||
Protected OnGotFocus handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.OnParentChanged(System.EventArgs)">
|
||||
<summary>
|
||||
Protected OnParentChanged handler.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.IsInitialized">
|
||||
<summary>
|
||||
True if initialization finished successfully and the control is not disposed yet.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GetSitedParentSite(System.Windows.Forms.Control)">
|
||||
<summary>
|
||||
Recursive retrieval of the parent control
|
||||
</summary>
|
||||
<param name="control">The control to get the parent for</param>
|
||||
<returns>The root parent control</returns>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2">
|
||||
<summary>
|
||||
The underlying CoreWebView2. Use this property to perform more operations on the WebView2 content than is exposed
|
||||
on the WebView2. This value is null until it is initialized and the object itself has undefined behaviour once the control is disposed.
|
||||
You can force the underlying CoreWebView2 to
|
||||
initialize via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.EnsureCoreWebView2Async(Microsoft.Web.WebView2.Core.CoreWebView2Environment,Microsoft.Web.WebView2.Core.CoreWebView2ControllerOptions)"/> method.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown if the calling thread isn't the thread which created this object (usually the UI thread). See <see cref="P:System.Windows.Forms.Control.InvokeRequired"/> for more info.</exception>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor">
|
||||
<summary>
|
||||
The zoom factor for the WebView.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.AllowExternalDrop">
|
||||
<summary>
|
||||
Enable/disable external drop.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.Source">
|
||||
<summary>
|
||||
The Source property is the URI of the top level document of the
|
||||
WebView2. Setting the Source is equivalent to calling <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>.
|
||||
Setting the Source will trigger initialization of the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/>, if not already initialized.
|
||||
The default value of Source is <c>null</c>, indicating that the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.
|
||||
</summary>
|
||||
<exception cref="T:System.ArgumentException">Specified value is not an absolute <see cref="T:System.Uri"/>.</exception>
|
||||
<exception cref="T:System.NotImplementedException">Specified value is <c>null</c> and the control is initialized.</exception>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Navigate(System.String)"/>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoForward">
|
||||
<summary>
|
||||
Returns true if the webview can navigate to a next page in the
|
||||
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward"/> method.
|
||||
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
|
||||
</summary>
|
||||
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoForward"/>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.CanGoBack">
|
||||
<summary>
|
||||
Returns <c>true</c> if the webview can navigate to a previous page in the
|
||||
navigation history via the <see cref="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack"/> method.
|
||||
This is equivalent to the <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this property is <c>false</c>.
|
||||
</summary>
|
||||
<seealso cref="P:Microsoft.Web.WebView2.Core.CoreWebView2.CanGoBack"/>
|
||||
</member>
|
||||
<member name="P:Microsoft.Web.WebView2.WinForms.WebView2.DefaultBackgroundColor">
|
||||
<summary>
|
||||
The default background color for the WebView.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.ExecuteScriptAsync(System.String)">
|
||||
<summary>
|
||||
Executes the provided script in the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.ExecuteScriptAsync(System.String)"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Reload">
|
||||
<summary>
|
||||
Reloads the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Reload"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoForward">
|
||||
<summary>
|
||||
Navigates to the next page in navigation history.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
|
||||
</summary>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoForward"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.GoBack">
|
||||
<summary>
|
||||
Navigates to the previous page in navigation history.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
|
||||
</summary>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.GoBack"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.NavigateToString(System.String)">
|
||||
<summary>
|
||||
Renders the provided HTML as the top level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>.
|
||||
</summary>
|
||||
<exception cref="T:System.InvalidOperationException">The underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized.</exception>
|
||||
<exception cref="T:System.InvalidOperationException">Thrown when browser process has unexpectedly and left this control in an invalid state. We are considering throwing a different type of exception for this case in the future.</exception>
|
||||
<remarks>The <c>htmlContent</c> parameter may not be larger than 2 MB (2 * 1024 * 1024 bytes) in total size. The origin of the new page is <c>about:blank</c>.</remarks>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.NavigateToString(System.String)"/>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.Stop">
|
||||
<summary>
|
||||
Stops any in progress navigation in the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to <see cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>.
|
||||
If the underlying <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> is not yet initialized, this method does nothing.
|
||||
</summary>
|
||||
<seealso cref="M:Microsoft.Web.WebView2.Core.CoreWebView2.Stop"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2InitializationCompleted">
|
||||
<summary>
|
||||
This event is triggered either 1) when the control's <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2"/> has finished being initialized (regardless of how it was triggered or whether it succeeded) but before it is used for anything
|
||||
OR 2) the initialization failed.
|
||||
You should handle this event if you need to perform one time setup operations on the CoreWebView2 which you want to affect all of its usages
|
||||
(e.g. adding event handlers, configuring settings, installing document creation scripts, adding host objects).
|
||||
</summary>
|
||||
<remarks>
|
||||
This sender will be the WebView2 control, whose CoreWebView2 property will now be valid (i.e. non-null) for the first time
|
||||
if <see cref="P:Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs.IsSuccess"/> is true.
|
||||
Unlikely this event can fire second time (after reporting initialization success first)
|
||||
if the initialization is followed by navigation which fails.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationStarting">
|
||||
<summary>
|
||||
NavigationStarting dispatches before a new navigate starts for the top
|
||||
level document of the <see cref="T:Microsoft.Web.WebView2.WinForms.WebView2"/>.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationStarting"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.NavigationCompleted">
|
||||
<summary>
|
||||
NavigationCompleted dispatches after a navigate of the top level
|
||||
document completes rendering either successfully or not.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.NavigationCompleted"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.WebMessageReceived">
|
||||
<summary>
|
||||
WebMessageReceived dispatches after web content sends a message to the
|
||||
app host via <c>chrome.webview.postMessage</c>.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.WebMessageReceived"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.SourceChanged">
|
||||
<summary>
|
||||
SourceChanged dispatches after the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.Source"/> property changes. This may happen
|
||||
during a navigation or if otherwise the script in the page changes the
|
||||
URI of the document.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.SourceChanged"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ContentLoading">
|
||||
<summary>
|
||||
ContentLoading dispatches after a navigation begins to a new URI and the
|
||||
content of that URI begins to render.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2.ContentLoading"/>
|
||||
</member>
|
||||
<member name="E:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactorChanged">
|
||||
<summary>
|
||||
ZoomFactorChanged dispatches when the <see cref="P:Microsoft.Web.WebView2.WinForms.WebView2.ZoomFactor"/> property changes.
|
||||
This is equivalent to the <see cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/> event.
|
||||
</summary>
|
||||
<seealso cref="E:Microsoft.Web.WebView2.Core.CoreWebView2Controller.ZoomFactorChanged"/>
|
||||
</member>
|
||||
<member name="F:Microsoft.Web.WebView2.WinForms.WebView2.components">
|
||||
<summary>
|
||||
Required designer variable.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Microsoft.Web.WebView2.WinForms.WebView2.InitializeComponent">
|
||||
<summary>
|
||||
Required method for Designer support - do not modify
|
||||
the contents of this method with the code editor.
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
File diff suppressed because it is too large
Load Diff
12
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/VisualStudioToolsManifest.xml
vendored
Normal file
12
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/VisualStudioToolsManifest.xml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<FileList>
|
||||
<File Reference = "Microsoft.Web.WebView2.Wpf.dll" >
|
||||
<ToolboxItems UIFramework="WPF" VSCategory="WebView2" BlendCategory="WebView2">
|
||||
<Item Type="Microsoft.Web.WebView2.Wpf.WebView2" />
|
||||
</ToolboxItems>
|
||||
</File>
|
||||
<File Reference = "Microsoft.Web.WebView2.WinForms.dll" >
|
||||
<ToolboxItems UIFramework="WinForms" VSCategory="WebView2">
|
||||
<Item Type="Microsoft.Web.WebView2.WinForms.WebView2" />
|
||||
</ToolboxItems>
|
||||
</File>
|
||||
</FileList>
|
||||
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/wv2winrt/winrt_winmd.winmd
vendored
Normal file
BIN
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/wv2winrt/winrt_winmd.winmd
vendored
Normal file
Binary file not shown.
14
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/wv2winrt/wv2winrt.exe.config
vendored
Normal file
14
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/wv2winrt/wv2winrt.exe.config
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
54
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/wv2winrt/wv2winrt.xml
vendored
Normal file
54
backend/packages/Microsoft.Web.WebView2.1.0.3595.46/tools/wv2winrt/wv2winrt.xml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>wv2winrt</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="M:wv2winrt.Program.Main(System.String[],System.String,System.String,System.Boolean,System.String[],System.String[],System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.String,System.Boolean)">
|
||||
<summary>
|
||||
Given WinMD files, produce C++ code that implements COM IDispatch wrappers around the types in those WinMD files.
|
||||
</summary>
|
||||
<remarks>
|
||||
There are several mechanisms to filter out types from the winmd that should not have code generated. They are
|
||||
applied in the following order of precedence:
|
||||
|
||||
1. If the requireAllowForWebAttribute switch is set, then only types
|
||||
with the AllowForWeb attribute will be considered.
|
||||
|
||||
2. If the ignoreWebHostHiddenAttribute switch is set, then types with
|
||||
the WebHostHidden attribute will still be included.
|
||||
|
||||
3. Explicit include and exclude rules sorted by the length of the
|
||||
rule string. So more general rules are applied first followed by
|
||||
more and more specific rules. If rule text matches the type or
|
||||
namespace name, then the type is included or excluded based on
|
||||
the kind of rule.
|
||||
|
||||
4. If the explicitIncludesOnly switch is set, only the types explicitly
|
||||
listed by the --include switch will be included.
|
||||
By default, wv2winrt includes all types
|
||||
referenced by other included types. This applies recursively, so
|
||||
a type included due to this will also include all types
|
||||
it references.
|
||||
|
||||
5. If there are no explicit include rules then a type that doesn't
|
||||
match any previous rule (1. to 4.) is included. Otherwise it is
|
||||
excluded.
|
||||
|
||||
</remarks>
|
||||
<param name="winmdPaths">Path(s) to winmd files containing types to generate code from/for.</param>
|
||||
<param name="outputPath">Path to a folder into which to place generated source code. Or `--` to write all generated code to standard out.</param>
|
||||
<param name="outputNamespace">Namespace where generated source code will be placed.</param>
|
||||
<param name="useFullNamespace">If set, wv2winrt will use full namespace names for dispatch adapter source files. Use this option when the output namespace is different to the consuming project's root namespace.</param>
|
||||
<param name="useJavascriptCase">If set, wv2winrt will use javascript casing for projection properties, methods, and events.</param>
|
||||
<param name="explicitIncludesOnly">If set, wv2winrt won't automatically follow references from other included types.</param>
|
||||
<param name="requireAllowForWebAttribute">If set, wv2winrt will only generate wrappers for runtimeclasses with the AllowForWeb attribute. By default, runtimeclasses with or without the AllowForWeb attribute will be included and will have wrapper code generated.</param>
|
||||
<param name="ignoreWebHostHiddenAttribute">If set, wv2winrt will generate wrappers for runtimeclasses with the WebHostHidden attribute. By default, runtimeclasses with the WebHostHidden attribute will be skipped and will not have generated wrapper code.</param>
|
||||
<param name="pch">Optional precompiled header filename of the project consuming the generated source code.</param>
|
||||
<param name="idl">Optional flag to generate only the idl file. Run with this option before MIDL if the consuming project defines its own WinRT types.</param>
|
||||
<param name="include">List of namespaces or runtimeclass names to be included in the generated output.</param>
|
||||
<param name="exclude">List of namespaces or runtimeclass names to be excluded from the generated output.</param>
|
||||
<param name="verbose">If set, generate verbose output including when a namespace or runtimeclass is excluded.</param>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
BIN
backend/packages/Newtonsoft.Json.13.0.4/.signature.p7s
vendored
Normal file
BIN
backend/packages/Newtonsoft.Json.13.0.4/.signature.p7s
vendored
Normal file
Binary file not shown.
20
backend/packages/Newtonsoft.Json.13.0.4/LICENSE.md
vendored
Normal file
20
backend/packages/Newtonsoft.Json.13.0.4/LICENSE.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2007 James Newton-King
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
BIN
backend/packages/Newtonsoft.Json.13.0.4/Newtonsoft.Json.13.0.4.nupkg
vendored
Normal file
BIN
backend/packages/Newtonsoft.Json.13.0.4/Newtonsoft.Json.13.0.4.nupkg
vendored
Normal file
Binary file not shown.
71
backend/packages/Newtonsoft.Json.13.0.4/README.md
vendored
Normal file
71
backend/packages/Newtonsoft.Json.13.0.4/README.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
#  Json.NET
|
||||
|
||||
[](https://www.nuget.org/packages/Newtonsoft.Json/)
|
||||
[](https://dev.azure.com/jamesnk/Public/_build/latest?definitionId=8)
|
||||
|
||||
Json.NET is a popular high-performance JSON framework for .NET
|
||||
|
||||
## Serialize JSON
|
||||
|
||||
```csharp
|
||||
Product product = new Product();
|
||||
product.Name = "Apple";
|
||||
product.Expiry = new DateTime(2008, 12, 28);
|
||||
product.Sizes = new string[] { "Small" };
|
||||
|
||||
string json = JsonConvert.SerializeObject(product);
|
||||
// {
|
||||
// "Name": "Apple",
|
||||
// "Expiry": "2008-12-28T00:00:00",
|
||||
// "Sizes": [
|
||||
// "Small"
|
||||
// ]
|
||||
// }
|
||||
```
|
||||
|
||||
## Deserialize JSON
|
||||
|
||||
```csharp
|
||||
string json = @"{
|
||||
'Name': 'Bad Boys',
|
||||
'ReleaseDate': '1995-4-7T00:00:00',
|
||||
'Genres': [
|
||||
'Action',
|
||||
'Comedy'
|
||||
]
|
||||
}";
|
||||
|
||||
Movie m = JsonConvert.DeserializeObject<Movie>(json);
|
||||
|
||||
string name = m.Name;
|
||||
// Bad Boys
|
||||
```
|
||||
|
||||
## LINQ to JSON
|
||||
|
||||
```csharp
|
||||
JArray array = new JArray();
|
||||
array.Add("Manual text");
|
||||
array.Add(new DateTime(2000, 5, 23));
|
||||
|
||||
JObject o = new JObject();
|
||||
o["MyArray"] = array;
|
||||
|
||||
string json = o.ToString();
|
||||
// {
|
||||
// "MyArray": [
|
||||
// "Manual text",
|
||||
// "2000-05-23T00:00:00"
|
||||
// ]
|
||||
// }
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [Homepage](https://www.newtonsoft.com/json)
|
||||
- [Documentation](https://www.newtonsoft.com/json/help)
|
||||
- [NuGet Package](https://www.nuget.org/packages/Newtonsoft.Json)
|
||||
- [Release Notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
|
||||
- [Contributing Guidelines](https://github.com/JamesNK/Newtonsoft.Json/blob/master/CONTRIBUTING.md)
|
||||
- [License](https://github.com/JamesNK/Newtonsoft.Json/blob/master/LICENSE.md)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/json.net)
|
||||
10713
backend/packages/Newtonsoft.Json.13.0.4/lib/net20/Newtonsoft.Json.xml
vendored
Normal file
10713
backend/packages/Newtonsoft.Json.13.0.4/lib/net20/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
9861
backend/packages/Newtonsoft.Json.13.0.4/lib/net35/Newtonsoft.Json.xml
vendored
Normal file
9861
backend/packages/Newtonsoft.Json.13.0.4/lib/net35/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10061
backend/packages/Newtonsoft.Json.13.0.4/lib/net40/Newtonsoft.Json.xml
vendored
Normal file
10061
backend/packages/Newtonsoft.Json.13.0.4/lib/net40/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11698
backend/packages/Newtonsoft.Json.13.0.4/lib/net45/Newtonsoft.Json.xml
vendored
Normal file
11698
backend/packages/Newtonsoft.Json.13.0.4/lib/net45/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11431
backend/packages/Newtonsoft.Json.13.0.4/lib/net6.0/Newtonsoft.Json.xml
vendored
Normal file
11431
backend/packages/Newtonsoft.Json.13.0.4/lib/net6.0/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11386
backend/packages/Newtonsoft.Json.13.0.4/lib/netstandard1.0/Newtonsoft.Json.xml
vendored
Normal file
11386
backend/packages/Newtonsoft.Json.13.0.4/lib/netstandard1.0/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11508
backend/packages/Newtonsoft.Json.13.0.4/lib/netstandard1.3/Newtonsoft.Json.xml
vendored
Normal file
11508
backend/packages/Newtonsoft.Json.13.0.4/lib/netstandard1.3/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11673
backend/packages/Newtonsoft.Json.13.0.4/lib/netstandard2.0/Newtonsoft.Json.xml
vendored
Normal file
11673
backend/packages/Newtonsoft.Json.13.0.4/lib/netstandard2.0/Newtonsoft.Json.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
backend/packages/Newtonsoft.Json.13.0.4/packageIcon.png
vendored
Normal file
BIN
backend/packages/Newtonsoft.Json.13.0.4/packageIcon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
4
backend/run.bat
Normal file
4
backend/run.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
@echo off
|
||||
echo Starting HMIWeb...
|
||||
start HMIWeb\bin\Debug\HMIWeb.exe
|
||||
echo Application started.
|
||||
24
frontend/.gitignore
vendored
Normal file
24
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
388
frontend/App.tsx
Normal file
388
frontend/App.tsx
Normal file
@@ -0,0 +1,388 @@
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
Activity, Settings, Move, Camera, Play, Square, RotateCw,
|
||||
Cpu, AlertTriangle, Siren, Terminal, Layers
|
||||
} from 'lucide-react';
|
||||
import { Machine3D } from './components/Machine3D';
|
||||
import { SettingsModal } from './components/SettingsModal';
|
||||
import { RecipePanel } from './components/RecipePanel';
|
||||
import { IOPanel } from './components/IOPanel';
|
||||
import { MotionPanel } from './components/MotionPanel';
|
||||
import { CameraPanel } from './components/CameraPanel';
|
||||
import { CyberPanel } from './components/common/CyberPanel';
|
||||
import { TechButton } from './components/common/TechButton';
|
||||
import { SystemState, Recipe, IOPoint, LogEntry, RobotTarget, ConfigItem } from './types';
|
||||
import { comms } from './communication';
|
||||
|
||||
// --- MOCK DATA ---
|
||||
const MOCK_RECIPES: Recipe[] = [
|
||||
{ id: '1', name: 'Wafer_Proc_300_Au', lastModified: '2023-10-25' },
|
||||
{ id: '2', name: 'Wafer_Insp_200_Adv', lastModified: '2023-10-26' },
|
||||
{ id: '3', name: 'Glass_Gen5_Bonding', lastModified: '2023-10-27' },
|
||||
];
|
||||
|
||||
const INITIAL_IO: IOPoint[] = [
|
||||
...Array.from({ length: 32 }, (_, i) => {
|
||||
let name = `DOUT_${i.toString().padStart(2, '0')}`;
|
||||
if (i === 0) name = "Tower Lamp Red";
|
||||
if (i === 1) name = "Tower Lamp Yel";
|
||||
if (i === 2) name = "Tower Lamp Grn";
|
||||
return { id: i, name, type: 'output' as const, state: false };
|
||||
}),
|
||||
...Array.from({ length: 32 }, (_, i) => {
|
||||
let name = `DIN_${i.toString().padStart(2, '0')}`;
|
||||
let initialState = false;
|
||||
if (i === 0) name = "Front Door Sensor";
|
||||
if (i === 1) name = "Right Door Sensor";
|
||||
if (i === 2) name = "Left Door Sensor";
|
||||
if (i === 3) name = "Back Door Sensor";
|
||||
if (i === 4) { name = "Main Air Pressure"; initialState = true; }
|
||||
if (i === 5) { name = "Vacuum Generator"; initialState = true; }
|
||||
if (i === 6) { name = "Emergency Stop Loop"; initialState = true; }
|
||||
return { id: i, name, type: 'input' as const, state: initialState };
|
||||
})
|
||||
];
|
||||
|
||||
// --- MAIN APP ---
|
||||
|
||||
export default function App() {
|
||||
const [activeTab, setActiveTab] = useState<'recipe' | 'io' | 'motion' | 'camera' | 'setting' | null>(null);
|
||||
const [systemState, setSystemState] = useState<SystemState>(SystemState.IDLE);
|
||||
const [currentRecipe, setCurrentRecipe] = useState<Recipe>(MOCK_RECIPES[0]);
|
||||
const [robotTarget, setRobotTarget] = useState<RobotTarget>({ x: 0, y: 0, z: 0 });
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const [ioPoints, setIoPoints] = useState<IOPoint[]>(INITIAL_IO);
|
||||
const [currentTime, setCurrentTime] = useState(new Date());
|
||||
const [config, setConfig] = useState<ConfigItem[] | null>(null);
|
||||
const [isConfigRefreshing, setIsConfigRefreshing] = useState(false);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const videoRef = useRef<any>(null);
|
||||
|
||||
// Check if running in WebView2 context
|
||||
const isWebView = typeof window !== 'undefined' && !!window.chrome?.webview;
|
||||
|
||||
// -- COMMUNICATION LAYER --
|
||||
useEffect(() => {
|
||||
// Subscribe to unified communication layer
|
||||
const unsubscribe = comms.subscribe((msg: any) => {
|
||||
if (!msg) return;
|
||||
|
||||
if (msg.type === 'STATUS_UPDATE') {
|
||||
// Update Motion State
|
||||
if (msg.position) {
|
||||
setRobotTarget({ x: msg.position.x, y: msg.position.y, z: msg.position.z });
|
||||
}
|
||||
// Update IO State (Merge with existing names/configs)
|
||||
if (msg.ioState) {
|
||||
setIoPoints(prev => {
|
||||
const newIO = [...prev];
|
||||
msg.ioState.forEach((update: { id: number, type: string, state: boolean }) => {
|
||||
const idx = newIO.findIndex(p => p.id === update.id && p.type === update.type);
|
||||
if (idx >= 0) newIO[idx] = { ...newIO[idx], state: update.state };
|
||||
});
|
||||
return newIO;
|
||||
});
|
||||
}
|
||||
// Update System State
|
||||
if (msg.sysState) {
|
||||
setSystemState(msg.sysState as SystemState);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addLog("COMMUNICATION CHANNEL OPEN", "info");
|
||||
|
||||
// Timer for Clock
|
||||
const timer = setInterval(() => setCurrentTime(new Date()), 1000);
|
||||
return () => {
|
||||
clearInterval(timer);
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// -- INITIALIZATION --
|
||||
useEffect(() => {
|
||||
const initSystem = async () => {
|
||||
// Just start up without fetching config
|
||||
addLog("SYSTEM STARTED", "info");
|
||||
setIsLoading(false);
|
||||
};
|
||||
initSystem();
|
||||
}, []);
|
||||
|
||||
// -- ON-DEMAND CONFIG FETCH --
|
||||
useEffect(() => {
|
||||
if (activeTab === 'setting') {
|
||||
const fetchConfig = async () => {
|
||||
setIsConfigRefreshing(true);
|
||||
try {
|
||||
const configStr = await comms.getConfig();
|
||||
setConfig(JSON.parse(configStr));
|
||||
addLog("CONFIG REFRESHED", "info");
|
||||
} catch (e) {
|
||||
addLog("CONFIG REFRESH FAILED", "error");
|
||||
}
|
||||
setIsConfigRefreshing(false);
|
||||
};
|
||||
fetchConfig();
|
||||
}
|
||||
}, [activeTab]);
|
||||
|
||||
const addLog = (msg: string, type: 'info' | 'warning' | 'error' = 'info') => {
|
||||
setLogs(prev => [{ id: Date.now() + Math.random(), timestamp: new Date().toLocaleTimeString(), message: msg, type }, ...prev].slice(0, 50));
|
||||
};
|
||||
|
||||
// Logic Helpers
|
||||
const doorStates = {
|
||||
front: ioPoints.find(p => p.id === 0 && p.type === 'input')?.state || false,
|
||||
right: ioPoints.find(p => p.id === 1 && p.type === 'input')?.state || false,
|
||||
left: ioPoints.find(p => p.id === 2 && p.type === 'input')?.state || false,
|
||||
back: ioPoints.find(p => p.id === 3 && p.type === 'input')?.state || false,
|
||||
};
|
||||
const isLowPressure = !(ioPoints.find(p => p.id === 4 && p.type === 'input')?.state ?? true);
|
||||
const isEmergencyStop = !(ioPoints.find(p => p.id === 6 && p.type === 'input')?.state ?? true);
|
||||
|
||||
// -- COMMAND HANDLERS --
|
||||
|
||||
// -- COMMAND HANDLERS --
|
||||
|
||||
const handleControl = async (action: 'start' | 'stop' | 'reset') => {
|
||||
if (isEmergencyStop && action === 'start') return addLog('EMERGENCY STOP ACTIVE', 'error');
|
||||
|
||||
try {
|
||||
await comms.sendControl(action.toUpperCase());
|
||||
addLog(`CMD SENT: ${action.toUpperCase()}`, 'info');
|
||||
} catch (e) {
|
||||
addLog('COMM ERROR', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const toggleIO = async (id: number, type: 'input' | 'output', forceState?: boolean) => {
|
||||
// Only allow output toggling
|
||||
if (type === 'output') {
|
||||
const current = ioPoints.find(p => p.id === id && p.type === type)?.state;
|
||||
const nextState = forceState !== undefined ? forceState : !current;
|
||||
await comms.setIO(id, nextState);
|
||||
}
|
||||
};
|
||||
|
||||
const moveAxis = async (axis: 'X' | 'Y' | 'Z', value: number) => {
|
||||
if (isEmergencyStop) return;
|
||||
await comms.moveAxis(axis, value);
|
||||
addLog(`CMD MOVE ${axis}: ${value}`, 'info');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab === 'camera' && navigator.mediaDevices?.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({ video: true }).then(s => { if (videoRef.current) videoRef.current.srcObject = s });
|
||||
}
|
||||
}, [activeTab]);
|
||||
|
||||
const handleSaveConfig = async (newConfig: ConfigItem[]) => {
|
||||
try {
|
||||
await comms.saveConfig(JSON.stringify(newConfig));
|
||||
setConfig(newConfig);
|
||||
addLog("CONFIGURATION SAVED", "success");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
addLog("FAILED TO SAVE CONFIG", "error");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-screen h-screen bg-slate-950 text-slate-100 overflow-hidden font-sans">
|
||||
|
||||
{/* Animated Nebula Background */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-slate-950 via-[#050a15] to-[#0a0f20] animate-gradient bg-[length:400%_400%] z-0"></div>
|
||||
<div className="absolute inset-0 grid-bg opacity-30 z-0"></div>
|
||||
<div className="absolute inset-0 scanlines z-50 pointer-events-none"></div>
|
||||
|
||||
{/* LOADING OVERLAY */}
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 z-[100] bg-black flex flex-col items-center justify-center gap-6">
|
||||
<div className="relative">
|
||||
<div className="w-24 h-24 border-4 border-neon-blue/30 rounded-full animate-spin"></div>
|
||||
<div className="absolute inset-0 border-t-4 border-neon-blue rounded-full animate-spin"></div>
|
||||
<Cpu className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-neon-blue w-10 h-10 animate-pulse" />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-tech font-bold text-white tracking-widest mb-2">SYSTEM INITIALIZING</h2>
|
||||
<p className="font-mono text-neon-blue text-sm animate-pulse">ESTABLISHING CONNECTION...</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* HEADER */}
|
||||
<header className="absolute top-0 left-0 right-0 h-20 px-6 flex items-center justify-between z-40 bg-gradient-to-b from-black/80 to-transparent pointer-events-none">
|
||||
<div className="flex items-center gap-4 pointer-events-auto">
|
||||
<div className="w-12 h-12 border-2 border-neon-blue flex items-center justify-center rounded shadow-glow-blue bg-black/50 backdrop-blur">
|
||||
<Cpu className="text-neon-blue w-8 h-8 animate-pulse-slow" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-tech font-bold text-white tracking-widest uppercase italic text-shadow-glow-blue">
|
||||
EQUI-HANDLER <span className="text-neon-blue text-sm not-italic">PRO</span>
|
||||
</h1>
|
||||
<div className="flex gap-2 text-[10px] text-neon-blue/70 font-mono">
|
||||
<span>SYS.VER 4.2.0</span>
|
||||
<span>|</span>
|
||||
<span className={isWebView ? "text-neon-green" : "text-amber-500"}>
|
||||
LINK: {isWebView ? "NATIVE" : "SIMULATION"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Top Navigation */}
|
||||
<div className="flex items-center gap-2 pointer-events-auto bg-black/40 backdrop-blur-md p-1 rounded-full border border-white/10">
|
||||
{[
|
||||
{ id: 'recipe', icon: Layers, label: 'RECIPE' },
|
||||
{ id: 'io', icon: Activity, label: 'I/O MONITOR' },
|
||||
{ id: 'motion', icon: Move, label: 'MOTION' },
|
||||
{ id: 'camera', icon: Camera, label: 'VISION' },
|
||||
{ id: 'setting', icon: Settings, label: 'CONFIG' }
|
||||
].map(item => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => setActiveTab(activeTab === item.id ? null : item.id as any)}
|
||||
className={`
|
||||
flex items-center gap-2 px-6 py-2 rounded-full font-tech font-bold text-sm transition-all border border-transparent
|
||||
${activeTab === item.id
|
||||
? 'bg-neon-blue/10 text-neon-blue border-neon-blue shadow-glow-blue'
|
||||
: 'text-slate-400 hover:text-white hover:bg-white/5'}
|
||||
`}
|
||||
>
|
||||
<item.icon className="w-4 h-4" /> {item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-right pointer-events-auto">
|
||||
<div className="text-2xl font-mono font-bold text-white text-shadow-glow-blue">
|
||||
{currentTime.toLocaleTimeString('en-GB')}
|
||||
</div>
|
||||
<div className="text-xs font-tech text-slate-400 tracking-[0.3em]">
|
||||
{currentTime.toLocaleDateString().toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* MAIN CONTENT */}
|
||||
<main className="absolute inset-0 pt-24 pb-12 px-6 flex gap-6 z-10">
|
||||
|
||||
{/* 3D Canvas (Background Layer) */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Machine3D target={robotTarget} ioState={ioPoints} doorStates={doorStates} />
|
||||
</div>
|
||||
|
||||
{/* Center Alarms */}
|
||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none flex flex-col items-center gap-4">
|
||||
{isEmergencyStop && (
|
||||
<div className="bg-red-600/90 text-white p-8 border-4 border-red-500 shadow-glow-red flex items-center gap-6 animate-pulse">
|
||||
<Siren className="w-16 h-16 animate-spin" />
|
||||
<div>
|
||||
<h1 className="text-5xl font-tech font-bold tracking-widest">EMERGENCY STOP</h1>
|
||||
<p className="text-center font-mono text-lg">SYSTEM HALTED - RELEASE TO RESET</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isLowPressure && !isEmergencyStop && (
|
||||
<div className="bg-amber-500/80 text-black px-8 py-4 rounded font-bold text-2xl tracking-widest flex items-center gap-4 shadow-glow-red animate-bounce">
|
||||
<AlertTriangle className="w-8 h-8" /> LOW AIR PRESSURE WARNING
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Floating Panel (Left) */}
|
||||
{activeTab && activeTab !== 'setting' && (
|
||||
<div className="w-[450px] z-20 animate-in slide-in-from-left-20 duration-500 fade-in">
|
||||
<CyberPanel className="h-full flex flex-col">
|
||||
{activeTab === 'recipe' && <RecipePanel recipes={MOCK_RECIPES} currentRecipe={currentRecipe} onSelectRecipe={(r) => { setCurrentRecipe(r); addLog(`LOADED: ${r.name}`) }} />}
|
||||
{activeTab === 'io' && <IOPanel ioPoints={ioPoints} onToggle={toggleIO} />}
|
||||
{activeTab === 'motion' && <MotionPanel robotTarget={robotTarget} onMove={moveAxis} />}
|
||||
{activeTab === 'camera' && <CameraPanel videoRef={videoRef} />}
|
||||
</CyberPanel>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Settings Modal */}
|
||||
<SettingsModal
|
||||
isOpen={activeTab === 'setting'}
|
||||
onClose={() => setActiveTab(null)}
|
||||
config={config}
|
||||
isRefreshing={isConfigRefreshing}
|
||||
onSave={handleSaveConfig}
|
||||
/>
|
||||
|
||||
{/* Right Sidebar (Dashboard) */}
|
||||
<div className="w-80 ml-auto z-20 flex flex-col gap-4">
|
||||
<CyberPanel className="flex-none">
|
||||
<div className="mb-2 text-xs text-neon-blue font-bold tracking-widest uppercase">System Status</div>
|
||||
<div className={`text-3xl font-tech font-bold mb-4 ${systemState === SystemState.RUNNING ? 'text-neon-green text-shadow-glow-green' : 'text-slate-400'}`}>
|
||||
{systemState}
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<TechButton
|
||||
variant="green"
|
||||
className="w-full flex items-center justify-center gap-2"
|
||||
active={systemState === SystemState.RUNNING}
|
||||
onClick={() => handleControl('start')}
|
||||
>
|
||||
<Play className="w-4 h-4" /> START AUTO
|
||||
</TechButton>
|
||||
<TechButton
|
||||
variant="amber"
|
||||
className="w-full flex items-center justify-center gap-2"
|
||||
active={systemState === SystemState.PAUSED}
|
||||
onClick={() => handleControl('stop')}
|
||||
>
|
||||
<Square className="w-4 h-4 fill-current" /> STOP / PAUSE
|
||||
</TechButton>
|
||||
<TechButton
|
||||
className="w-full flex items-center justify-center gap-2"
|
||||
onClick={() => handleControl('reset')}
|
||||
>
|
||||
<RotateCw className="w-4 h-4" /> SYSTEM RESET
|
||||
</TechButton>
|
||||
</div>
|
||||
</CyberPanel>
|
||||
|
||||
<CyberPanel className="flex-1 flex flex-col min-h-0">
|
||||
<div className="mb-2 flex items-center justify-between text-xs text-neon-blue font-bold tracking-widest uppercase border-b border-white/10 pb-2">
|
||||
<span>Event Log</span>
|
||||
<Terminal className="w-3 h-3" />
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto font-mono text-[10px] space-y-1 pr-1 custom-scrollbar">
|
||||
{logs.map(log => (
|
||||
<div key={log.id} className={`flex gap-2 ${log.type === 'error' ? 'text-red-500' : log.type === 'warning' ? 'text-amber-400' : 'text-slate-400'}`}>
|
||||
<span className="opacity-50">[{log.timestamp}]</span>
|
||||
<span>{log.message}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CyberPanel>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
{/* FOOTER STATUS */}
|
||||
<footer className="absolute bottom-0 left-0 right-0 h-10 bg-black/80 border-t border-neon-blue/30 flex items-center px-6 justify-between z-40 backdrop-blur text-xs font-mono text-slate-400">
|
||||
<div className="flex gap-6">
|
||||
{['PLC', 'MOTION', 'VISION', 'LIGHT'].map(hw => (
|
||||
<div key={hw} className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-neon-green rounded-full shadow-[0_0_5px_#0aff00]"></div>
|
||||
<span className="font-bold text-slate-300">{hw}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-8 text-neon-blue">
|
||||
<span>POS.X: {robotTarget.x.toFixed(3)}</span>
|
||||
<span>POS.Y: {robotTarget.y.toFixed(3)}</span>
|
||||
<span>POS.Z: {robotTarget.z.toFixed(3)}</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
20
frontend/README.md
Normal file
20
frontend/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
<div align="center">
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/drive/1WUaQ4phJ_kZujzyiakV5X8RFgCwgaUDq
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
10
frontend/build.bat
Normal file
10
frontend/build.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
echo Building Frontend...
|
||||
call npm run build
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
echo Build Failed!
|
||||
pause
|
||||
exit /b %ERRORLEVEL%
|
||||
)
|
||||
echo Build Success!
|
||||
pause
|
||||
131
frontend/communication.ts
Normal file
131
frontend/communication.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
// Check if running in WebView2
|
||||
const isWebView = typeof window !== 'undefined' && !!window.chrome?.webview;
|
||||
|
||||
type MessageCallback = (data: any) => void;
|
||||
|
||||
class CommunicationLayer {
|
||||
private listeners: MessageCallback[] = [];
|
||||
private ws: WebSocket | null = null;
|
||||
private isConnected = false;
|
||||
|
||||
constructor() {
|
||||
if (isWebView) {
|
||||
console.log("[COMM] Running in WebView2 Mode");
|
||||
window.chrome.webview.addEventListener('message', (event: any) => {
|
||||
this.notifyListeners(event.data);
|
||||
});
|
||||
} else {
|
||||
console.log("[COMM] Running in Browser Mode (WebSocket)");
|
||||
this.connectWebSocket();
|
||||
}
|
||||
}
|
||||
|
||||
private connectWebSocket() {
|
||||
this.ws = new WebSocket('ws://localhost:8081');
|
||||
|
||||
this.ws.onopen = () => {
|
||||
console.log("[COMM] WebSocket Connected");
|
||||
this.isConnected = true;
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.notifyListeners(data);
|
||||
} catch (e) {
|
||||
console.error("[COMM] JSON Parse Error", e);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
console.log("[COMM] WebSocket Closed. Reconnecting...");
|
||||
this.isConnected = false;
|
||||
setTimeout(() => this.connectWebSocket(), 2000);
|
||||
};
|
||||
|
||||
this.ws.onerror = (err) => {
|
||||
console.error("[COMM] WebSocket Error", err);
|
||||
};
|
||||
}
|
||||
|
||||
private notifyListeners(data: any) {
|
||||
this.listeners.forEach(cb => cb(data));
|
||||
}
|
||||
|
||||
public subscribe(callback: MessageCallback) {
|
||||
this.listeners.push(callback);
|
||||
return () => {
|
||||
this.listeners = this.listeners.filter(cb => cb !== callback);
|
||||
};
|
||||
}
|
||||
|
||||
// --- API Methods ---
|
||||
|
||||
public async getConfig(): Promise<string> {
|
||||
if (isWebView) {
|
||||
return await window.chrome.webview.hostObjects.machine.GetConfig();
|
||||
} else {
|
||||
// WebSocket Request/Response Pattern
|
||||
return new Promise((resolve, reject) => {
|
||||
// 1. Wait for Connection (max 2s)
|
||||
if (!this.isConnected) {
|
||||
// Simple wait logic (could be improved)
|
||||
setTimeout(() => {
|
||||
if (!this.isConnected) reject("WebSocket connection timeout");
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 2. Send Request with Timeout (max 10s)
|
||||
const timeoutId = setTimeout(() => {
|
||||
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||
reject("Config fetch timeout");
|
||||
}, 10000);
|
||||
|
||||
const handler = (data: any) => {
|
||||
if (data.type === 'CONFIG_DATA') {
|
||||
clearTimeout(timeoutId);
|
||||
this.listeners = this.listeners.filter(cb => cb !== handler);
|
||||
resolve(JSON.stringify(data.data));
|
||||
}
|
||||
};
|
||||
this.listeners.push(handler);
|
||||
this.ws?.send(JSON.stringify({ type: 'GET_CONFIG' }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async saveConfig(configJson: string): Promise<void> {
|
||||
if (isWebView) {
|
||||
await window.chrome.webview.hostObjects.machine.SaveConfig(configJson);
|
||||
} else {
|
||||
this.ws?.send(JSON.stringify({ type: 'SAVE_CONFIG', data: JSON.parse(configJson) }));
|
||||
}
|
||||
}
|
||||
|
||||
public async sendControl(command: string) {
|
||||
if (isWebView) {
|
||||
await window.chrome.webview.hostObjects.machine.SystemControl(command);
|
||||
} else {
|
||||
this.ws?.send(JSON.stringify({ type: 'CONTROL', command }));
|
||||
}
|
||||
}
|
||||
|
||||
public async moveAxis(axis: string, value: number) {
|
||||
if (isWebView) {
|
||||
await window.chrome.webview.hostObjects.machine.MoveAxis(axis, value);
|
||||
} else {
|
||||
this.ws?.send(JSON.stringify({ type: 'MOVE', axis, value }));
|
||||
}
|
||||
}
|
||||
|
||||
public async setIO(id: number, state: boolean) {
|
||||
if (isWebView) {
|
||||
await window.chrome.webview.hostObjects.machine.SetIO(id, false, state);
|
||||
} else {
|
||||
this.ws?.send(JSON.stringify({ type: 'SET_IO', id, state }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const comms = new CommunicationLayer();
|
||||
39
frontend/components/CameraPanel.tsx
Normal file
39
frontend/components/CameraPanel.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Camera, Crosshair } from 'lucide-react';
|
||||
import { PanelHeader } from './common/PanelHeader';
|
||||
|
||||
interface CameraPanelProps {
|
||||
videoRef: React.RefObject<HTMLVideoElement>;
|
||||
}
|
||||
|
||||
export const CameraPanel: React.FC<CameraPanelProps> = ({ videoRef }) => (
|
||||
<div className="h-full flex flex-col">
|
||||
<PanelHeader title="Vision Feed" icon={Camera} />
|
||||
<div className="flex-1 bg-black relative overflow-hidden border border-slate-800 group">
|
||||
<video ref={videoRef} autoPlay playsInline className="w-full h-full object-cover opacity-80" />
|
||||
|
||||
{/* HUD OVERLAY */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-full h-full border-[20px] border-neon-blue/10 clip-tech-inv"></div>
|
||||
<Crosshair className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-12 h-12 text-neon-blue opacity-50" />
|
||||
|
||||
<div className="absolute top-4 right-4 flex flex-col items-end gap-1">
|
||||
<span className="text-[10px] font-mono text-neon-red animate-pulse">● REC</span>
|
||||
<span className="text-xs font-mono text-neon-blue">1920x1080 @ 60FPS</span>
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-4 left-4 text-neon-blue/80 font-mono text-xs">
|
||||
EXPOSURE: AUTO<br />
|
||||
GAIN: 12dB<br />
|
||||
FOCUS: 150mm
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-2 mt-3">
|
||||
<button className="bg-slate-800 text-slate-400 text-[10px] py-2 hover:bg-neon-blue hover:text-black transition-colors">ZOOM +</button>
|
||||
<button className="bg-slate-800 text-slate-400 text-[10px] py-2 hover:bg-neon-blue hover:text-black transition-colors">ZOOM -</button>
|
||||
<button className="bg-slate-800 text-slate-400 text-[10px] py-2 hover:bg-neon-blue hover:text-black transition-colors">SNAP</button>
|
||||
<button className="bg-slate-800 text-slate-400 text-[10px] py-2 hover:bg-neon-blue hover:text-black transition-colors">SETTINGS</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
50
frontend/components/IOPanel.tsx
Normal file
50
frontend/components/IOPanel.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Activity } from 'lucide-react';
|
||||
import { IOPoint } from '../types';
|
||||
import { PanelHeader } from './common/PanelHeader';
|
||||
import { TechButton } from './common/TechButton';
|
||||
|
||||
interface IOPanelProps {
|
||||
ioPoints: IOPoint[];
|
||||
onToggle: (id: number, type: 'input' | 'output') => void;
|
||||
}
|
||||
|
||||
export const IOPanel: React.FC<IOPanelProps> = ({ ioPoints, onToggle }) => {
|
||||
const [tab, setTab] = useState<'in' | 'out'>('in');
|
||||
const points = ioPoints.filter(p => p.type === (tab === 'in' ? 'input' : 'output'));
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<PanelHeader title="I/O Monitor" icon={Activity} />
|
||||
<div className="flex gap-2 mb-4">
|
||||
<TechButton active={tab === 'in'} onClick={() => setTab('in')} className="flex-1">Inputs</TechButton>
|
||||
<TechButton active={tab === 'out'} onClick={() => setTab('out')} className="flex-1" variant="blue">Outputs</TechButton>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-2 overflow-y-auto pr-2 custom-scrollbar pb-4">
|
||||
{points.map(p => (
|
||||
<div
|
||||
key={p.id}
|
||||
onClick={() => onToggle(p.id, p.type)}
|
||||
className={`
|
||||
aspect-square flex flex-col items-center justify-center p-1 cursor-pointer transition-all border
|
||||
clip-tech
|
||||
${p.state
|
||||
? (p.type === 'output'
|
||||
? 'bg-neon-green/10 border-neon-green text-neon-green shadow-[0_0_10px_rgba(10,255,0,0.3)]'
|
||||
: 'bg-neon-yellow/10 border-neon-yellow text-neon-yellow shadow-[0_0_10px_rgba(255,230,0,0.3)]')
|
||||
: 'bg-slate-900/50 border-slate-700 text-slate-600 hover:border-slate-500'}
|
||||
`}
|
||||
>
|
||||
<div className={`w-2 h-2 rounded-full mb-2 ${p.state ? (p.type === 'output' ? 'bg-neon-green' : 'bg-neon-yellow') : 'bg-slate-800'}`}></div>
|
||||
<span className="font-mono text-[10px] font-bold">
|
||||
{p.type === 'input' ? 'I' : 'Q'}{p.id.toString().padStart(2, '0')}
|
||||
</span>
|
||||
<span className="text-[8px] text-center uppercase leading-tight mt-1 opacity-80 truncate w-full px-1">
|
||||
{p.name.replace(/(Sensor|Door|Lamp)/g, '')}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
633
frontend/components/Machine3D.tsx
Normal file
633
frontend/components/Machine3D.tsx
Normal file
@@ -0,0 +1,633 @@
|
||||
|
||||
import React, { useRef, useMemo } from 'react';
|
||||
import { Canvas, useFrame } from '@react-three/fiber';
|
||||
import { OrbitControls, Grid, PerspectiveCamera, Text, Box, Environment, RoundedBox } from '@react-three/drei';
|
||||
import * as THREE from 'three';
|
||||
import { RobotTarget, IOPoint } from '../types';
|
||||
|
||||
interface Machine3DProps {
|
||||
target: RobotTarget;
|
||||
ioState: IOPoint[];
|
||||
doorStates: {
|
||||
front: boolean;
|
||||
right: boolean;
|
||||
left: boolean;
|
||||
back: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// -- Parts Components --
|
||||
|
||||
const TowerLamp = ({ ioState }: { ioState: IOPoint[] }) => {
|
||||
// Outputs 0,1,2 mapped to Red, Yellow, Green
|
||||
const redOn = ioState.find(io => io.id === 0 && io.type === 'output')?.state;
|
||||
const yellowOn = ioState.find(io => io.id === 1 && io.type === 'output')?.state;
|
||||
const greenOn = ioState.find(io => io.id === 2 && io.type === 'output')?.state;
|
||||
|
||||
const redMat = useRef<THREE.MeshStandardMaterial>(null);
|
||||
const yelMat = useRef<THREE.MeshStandardMaterial>(null);
|
||||
const grnMat = useRef<THREE.MeshStandardMaterial>(null);
|
||||
|
||||
useFrame((state) => {
|
||||
const time = state.clock.elapsedTime;
|
||||
const pulse = (Math.sin(time * 6) * 0.5 + 0.5);
|
||||
const intensity = 0.5 + (pulse * 3.0);
|
||||
|
||||
if (redMat.current) {
|
||||
redMat.current.emissiveIntensity = redOn ? intensity : 0;
|
||||
redMat.current.opacity = redOn ? 1.0 : 0.3;
|
||||
redMat.current.color.setHex(redOn ? 0xff0000 : 0x550000);
|
||||
}
|
||||
if (yelMat.current) {
|
||||
yelMat.current.emissiveIntensity = yellowOn ? intensity : 0;
|
||||
yelMat.current.opacity = yellowOn ? 1.0 : 0.3;
|
||||
yelMat.current.color.setHex(yellowOn ? 0xffff00 : 0x555500);
|
||||
}
|
||||
if (grnMat.current) {
|
||||
grnMat.current.emissiveIntensity = greenOn ? intensity : 0;
|
||||
grnMat.current.opacity = greenOn ? 1.0 : 0.3;
|
||||
grnMat.current.color.setHex(greenOn ? 0x00ff00 : 0x005500);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group position={[1.8, 2.5, -1.8]}>
|
||||
{/* Pole */}
|
||||
<mesh position={[0, 0, 0]}>
|
||||
<cylinderGeometry args={[0.05, 0.05, 0.5]} />
|
||||
<meshStandardMaterial color="#333" roughness={0.5} />
|
||||
</mesh>
|
||||
{/* Green */}
|
||||
<mesh position={[0, 0.3, 0]}>
|
||||
<cylinderGeometry args={[0.08, 0.08, 0.15]} />
|
||||
<meshStandardMaterial
|
||||
ref={grnMat}
|
||||
color="#00ff00"
|
||||
emissive="#00ff00"
|
||||
transparent
|
||||
toneMapped={false}
|
||||
/>
|
||||
</mesh>
|
||||
{/* Yellow */}
|
||||
<mesh position={[0, 0.46, 0]}>
|
||||
<cylinderGeometry args={[0.08, 0.08, 0.15]} />
|
||||
<meshStandardMaterial
|
||||
ref={yelMat}
|
||||
color="#ffff00"
|
||||
emissive="#ffff00"
|
||||
transparent
|
||||
toneMapped={false}
|
||||
/>
|
||||
</mesh>
|
||||
{/* Red */}
|
||||
<mesh position={[0, 0.62, 0]}>
|
||||
<cylinderGeometry args={[0.08, 0.08, 0.15]} />
|
||||
<meshStandardMaterial
|
||||
ref={redMat}
|
||||
color="#ff0000"
|
||||
emissive="#ff0000"
|
||||
transparent
|
||||
toneMapped={false}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
// -- DIPPING STATION COMPONENTS --
|
||||
|
||||
const LiquidBath = ({
|
||||
position,
|
||||
label,
|
||||
liquidColor,
|
||||
}: {
|
||||
position: [number, number, number],
|
||||
label: string,
|
||||
liquidColor: string,
|
||||
}) => {
|
||||
const width = 1.0;
|
||||
const depth = 0.8;
|
||||
const height = 0.3;
|
||||
const wallThick = 0.03;
|
||||
|
||||
const SteelMaterial = (
|
||||
<meshStandardMaterial color="#e2e8f0" metalness={0.95} roughness={0.2} />
|
||||
);
|
||||
|
||||
return (
|
||||
<group position={position}>
|
||||
<group position={[0, height / 2, 0]}>
|
||||
<mesh position={[0, -height/2 + wallThick/2, 0]}>
|
||||
<boxGeometry args={[width, wallThick, depth]} />
|
||||
{SteelMaterial}
|
||||
</mesh>
|
||||
<mesh position={[0, 0, depth/2 - wallThick/2]}>
|
||||
<boxGeometry args={[width, height, wallThick]} />
|
||||
{SteelMaterial}
|
||||
</mesh>
|
||||
<mesh position={[0, 0, -depth/2 + wallThick/2]}>
|
||||
<boxGeometry args={[width, height, wallThick]} />
|
||||
{SteelMaterial}
|
||||
</mesh>
|
||||
<mesh position={[-width/2 + wallThick/2, 0, 0]}>
|
||||
<boxGeometry args={[wallThick, height, depth - wallThick*2]} />
|
||||
{SteelMaterial}
|
||||
</mesh>
|
||||
<mesh position={[width/2 - wallThick/2, 0, 0]}>
|
||||
<boxGeometry args={[wallThick, height, depth - wallThick*2]} />
|
||||
{SteelMaterial}
|
||||
</mesh>
|
||||
<mesh position={[0, height/2 - 0.05, 0]} rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<planeGeometry args={[width - wallThick*2, depth - wallThick*2]} />
|
||||
<meshPhysicalMaterial
|
||||
color={liquidColor}
|
||||
transparent
|
||||
opacity={0.85}
|
||||
roughness={0.05}
|
||||
metalness={0.2}
|
||||
transmission={0.4}
|
||||
thickness={1}
|
||||
clearcoat={1}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
<Text
|
||||
position={[0, 0.6, 0.55]}
|
||||
fontSize={0.12}
|
||||
color="white"
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
rotation={[-Math.PI/4, 0, 0]}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
const HakkoFX305 = ({ position }: { position: [number, number, number] }) => {
|
||||
return (
|
||||
<group position={position}>
|
||||
{/* Main Blue Chassis */}
|
||||
<mesh position={[0, 0.25, 0]}>
|
||||
<boxGeometry args={[1.3, 0.55, 1.0]} />
|
||||
<meshStandardMaterial color="#2563eb" metalness={0.1} roughness={0.4} />
|
||||
</mesh>
|
||||
|
||||
<mesh position={[-0.66, 0.25, 0]}>
|
||||
<boxGeometry args={[0.02, 0.3, 0.6]} />
|
||||
<meshStandardMaterial color="#1e3a8a" />
|
||||
</mesh>
|
||||
<mesh position={[0.66, 0.25, 0]}>
|
||||
<boxGeometry args={[0.02, 0.3, 0.6]} />
|
||||
<meshStandardMaterial color="#1e3a8a" />
|
||||
</mesh>
|
||||
|
||||
<mesh position={[0, 0.25, 0.51]}>
|
||||
<planeGeometry args={[1.25, 0.5]} />
|
||||
<meshStandardMaterial color="#cbd5e1" metalness={0.6} roughness={0.3} />
|
||||
</mesh>
|
||||
|
||||
<Text
|
||||
position={[0, 0.42, 0.52]}
|
||||
fontSize={0.08}
|
||||
color="#2563eb"
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
fontWeight="bold"
|
||||
>
|
||||
HAKKO
|
||||
</Text>
|
||||
<Text
|
||||
position={[0.5, 0.42, 0.52]}
|
||||
fontSize={0.04}
|
||||
color="#64748b"
|
||||
anchorX="right"
|
||||
anchorY="middle"
|
||||
>
|
||||
FX-305
|
||||
</Text>
|
||||
|
||||
<mesh position={[0.15, 0.25, 0.52]}>
|
||||
<planeGeometry args={[0.35, 0.15]} />
|
||||
<meshStandardMaterial color="#0f172a" roughness={0.2} />
|
||||
</mesh>
|
||||
|
||||
<group position={[-0.45, 0.25, 0.52]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<mesh>
|
||||
<cylinderGeometry args={[0.08, 0.08, 0.02, 32]} />
|
||||
<meshStandardMaterial color="#1e293b" />
|
||||
</mesh>
|
||||
<mesh position={[0, 0.02, 0]} rotation={[0, 0, Math.PI/2]}>
|
||||
<boxGeometry args={[0.02, 0.08, 0.01]} />
|
||||
<meshStandardMaterial color="#475569" />
|
||||
</mesh>
|
||||
</group>
|
||||
|
||||
<group position={[0.15, 0.12, 0.52]}>
|
||||
{[-0.12, 0, 0.12, 0.24].map((x, i) => (
|
||||
<mesh key={i} position={[x, 0, 0]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<cylinderGeometry args={[0.035, 0.035, 0.02, 16]} />
|
||||
<meshStandardMaterial color="#facc15" />
|
||||
</mesh>
|
||||
))}
|
||||
</group>
|
||||
|
||||
<mesh position={[0, 0.55, 0]}>
|
||||
<boxGeometry args={[0.9, 0.05, 0.7]} />
|
||||
<meshStandardMaterial color="#94a3b8" metalness={0.8} roughness={0.3} />
|
||||
</mesh>
|
||||
|
||||
<group position={[0, 0.58, 0]}>
|
||||
<mesh position={[0, 0, -0.3]}>
|
||||
<boxGeometry args={[0.8, 0.05, 0.05]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.7} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0, 0.3]}>
|
||||
<boxGeometry args={[0.8, 0.05, 0.05]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.7} />
|
||||
</mesh>
|
||||
<mesh position={[-0.4, 0, 0]}>
|
||||
<boxGeometry args={[0.05, 0.05, 0.65]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.7} />
|
||||
</mesh>
|
||||
<mesh position={[0.4, 0, 0]}>
|
||||
<boxGeometry args={[0.05, 0.05, 0.65]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.7} />
|
||||
</mesh>
|
||||
</group>
|
||||
|
||||
<mesh position={[0, 0.57, 0]} rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<planeGeometry args={[0.75, 0.6]} />
|
||||
<meshPhysicalMaterial
|
||||
color="#e2e8f0"
|
||||
emissive="#cbd5e1"
|
||||
emissiveIntensity={0.1}
|
||||
roughness={0.02}
|
||||
metalness={1.0}
|
||||
clearcoat={1}
|
||||
reflectivity={1}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
<Text
|
||||
position={[0, 0.9, 0.6]}
|
||||
fontSize={0.15}
|
||||
color="#fbbf24"
|
||||
anchorX="center"
|
||||
anchorY="middle"
|
||||
rotation={[-Math.PI/4, 0, 0]}
|
||||
>
|
||||
2. HAKKO SOLDER
|
||||
</Text>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
const DippingStations = () => {
|
||||
return (
|
||||
<group>
|
||||
<mesh position={[0, 0.2, 0]}>
|
||||
<boxGeometry args={[4.5, 0.4, 1.5]} />
|
||||
<meshStandardMaterial color="#334155" roughness={0.5} metalness={0.5} />
|
||||
</mesh>
|
||||
<LiquidBath position={[-1.5, 0.4, 0]} label="1. FLUX" liquidColor="#fef08a" />
|
||||
<HakkoFX305 position={[0, 0.4, 0]} />
|
||||
<LiquidBath position={[1.5, 0.4, 0]} label="3. CLEAN" liquidColor="#bae6fd" />
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
const Door = ({
|
||||
position,
|
||||
rotation = [0,0,0],
|
||||
size,
|
||||
isOpen
|
||||
}: {
|
||||
position: [number, number, number],
|
||||
rotation?: [number, number, number],
|
||||
size: [number, number],
|
||||
isOpen: boolean
|
||||
}) => {
|
||||
const meshRef = useRef<THREE.Mesh>(null);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (!meshRef.current) return;
|
||||
const targetY = isOpen ? position[1] + 2 : position[1];
|
||||
meshRef.current.position.y = THREE.MathUtils.lerp(meshRef.current.position.y, targetY, delta * 5);
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={meshRef} position={position} rotation={rotation as any}>
|
||||
<planeGeometry args={[size[0], size[1]]} />
|
||||
<meshPhysicalMaterial
|
||||
color="#a5f3fc"
|
||||
transparent
|
||||
opacity={0.2}
|
||||
roughness={0}
|
||||
metalness={0.9}
|
||||
side={THREE.DoubleSide}
|
||||
depthWrite={false}
|
||||
/>
|
||||
<mesh position={[size[0]/2 - 0.1, 0, 0.02]}>
|
||||
<boxGeometry args={[0.05, size[1], 0.05]} />
|
||||
<meshStandardMaterial color="#cbd5e1" />
|
||||
</mesh>
|
||||
<mesh position={[-size[0]/2 + 0.1, 0, 0.02]}>
|
||||
<boxGeometry args={[0.05, size[1], 0.05]} />
|
||||
<meshStandardMaterial color="#cbd5e1" />
|
||||
</mesh>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
const MachineFrame = ({ doorStates }: { doorStates: { front: boolean, right: boolean, left: boolean, back: boolean } }) => {
|
||||
return (
|
||||
<group>
|
||||
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.05, 0]}>
|
||||
<planeGeometry args={[6, 6]} />
|
||||
<meshStandardMaterial color="#0f172a" roughness={0.8} metalness={0.2} />
|
||||
</mesh>
|
||||
<Grid infiniteGrid fadeDistance={15} sectionColor="#475569" cellColor="#1e293b" />
|
||||
|
||||
{[[-2, -2], [2, -2], [2, 2], [-2, 2]].map(([x, z], i) => (
|
||||
<mesh key={i} position={[x, 1.5, z]}>
|
||||
<boxGeometry args={[0.15, 3, 0.15]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.8} roughness={0.2} />
|
||||
</mesh>
|
||||
))}
|
||||
|
||||
<group position={[0, 3, 0]}>
|
||||
<mesh position={[0, 0, -2]}>
|
||||
<boxGeometry args={[4.15, 0.15, 0.15]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.8} roughness={0.2} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0, 2]}>
|
||||
<boxGeometry args={[4.15, 0.15, 0.15]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.8} roughness={0.2} />
|
||||
</mesh>
|
||||
<mesh position={[-2, 0, 0]}>
|
||||
<boxGeometry args={[0.15, 0.15, 4.15]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.8} roughness={0.2} />
|
||||
</mesh>
|
||||
<mesh position={[2, 0, 0]}>
|
||||
<boxGeometry args={[0.15, 0.15, 4.15]} />
|
||||
<meshStandardMaterial color="#64748b" metalness={0.8} roughness={0.2} />
|
||||
</mesh>
|
||||
</group>
|
||||
|
||||
<Door position={[0, 1.5, 2]} size={[4, 3]} isOpen={doorStates.front} />
|
||||
<Door position={[0, 1.5, -2]} size={[4, 3]} isOpen={doorStates.back} />
|
||||
<Door position={[-2, 1.5, 0]} rotation={[0, Math.PI/2, 0]} size={[4, 3]} isOpen={doorStates.left} />
|
||||
<Door position={[2, 1.5, 0]} rotation={[0, Math.PI/2, 0]} size={[4, 3]} isOpen={doorStates.right} />
|
||||
<DippingStations />
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
// --- MISUMI STYLE INDUSTRIAL ACTUATOR COMPONENT ---
|
||||
|
||||
const IndustrialActuatorRail = ({ length, label, hasMotor = true }: { length: number, label?: string, hasMotor?: boolean }) => {
|
||||
const width = 0.14; // Actuator Width
|
||||
const height = 0.08; // Actuator Height (Profile)
|
||||
|
||||
return (
|
||||
<group>
|
||||
{/* 1. Main Aluminum Body (White/Light Grey Matte) */}
|
||||
<RoundedBox args={[width, height, length]} radius={0.01} smoothness={4}>
|
||||
<meshStandardMaterial color="#f8fafc" roughness={0.8} metalness={0.1} />
|
||||
</RoundedBox>
|
||||
|
||||
{/* 2. Top Stainless Steel Cover Strip */}
|
||||
<mesh position={[0, height/2 + 0.001, 0]}>
|
||||
<boxGeometry args={[width * 0.7, 0.002, length]} />
|
||||
<meshStandardMaterial color="#cbd5e1" metalness={0.9} roughness={0.2} />
|
||||
</mesh>
|
||||
|
||||
{/* 3. Black Gap/Slit for Slider */}
|
||||
<mesh position={[0, height/2, 0]}>
|
||||
<boxGeometry args={[width * 0.8, 0.01, length * 0.98]} />
|
||||
<meshStandardMaterial color="#0f172a" roughness={0.9} />
|
||||
</mesh>
|
||||
|
||||
{/* 4. End Caps (Plastic White) */}
|
||||
<group position={[0, 0, length/2 + 0.02]}>
|
||||
<RoundedBox args={[width + 0.01, height + 0.01, 0.04]} radius={0.02} smoothness={4}>
|
||||
<meshStandardMaterial color="#e2e8f0" />
|
||||
</RoundedBox>
|
||||
</group>
|
||||
<group position={[0, 0, -length/2 - 0.02]}>
|
||||
<RoundedBox args={[width + 0.01, height + 0.01, 0.04]} radius={0.02} smoothness={4}>
|
||||
<meshStandardMaterial color="#e2e8f0" />
|
||||
</RoundedBox>
|
||||
</group>
|
||||
|
||||
{/* 5. Servo Motor Unit (Black & Silver) */}
|
||||
{hasMotor && (
|
||||
<group position={[0, 0, -length/2 - 0.15]}>
|
||||
{/* Flange */}
|
||||
<mesh position={[0, 0, 0.08]}>
|
||||
<boxGeometry args={[0.12, 0.12, 0.05]} />
|
||||
<meshStandardMaterial color="#475569" metalness={0.8} />
|
||||
</mesh>
|
||||
{/* Motor Body */}
|
||||
<mesh position={[0, 0, 0]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<cylinderGeometry args={[0.05, 0.05, 0.2, 32]} />
|
||||
<meshStandardMaterial color="#1e293b" metalness={0.6} roughness={0.4} />
|
||||
</mesh>
|
||||
{/* Encoder Cap */}
|
||||
<mesh position={[0, 0, -0.11]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<cylinderGeometry args={[0.045, 0.045, 0.05, 32]} />
|
||||
<meshStandardMaterial color="#000000" />
|
||||
</mesh>
|
||||
{/* Cable Gland */}
|
||||
<mesh position={[0, 0.04, -0.05]}>
|
||||
<boxGeometry args={[0.03, 0.03, 0.03]} />
|
||||
<meshStandardMaterial color="#333" />
|
||||
</mesh>
|
||||
</group>
|
||||
)}
|
||||
|
||||
{/* 6. Label/Branding */}
|
||||
{label && (
|
||||
<Text
|
||||
position={[width/2 + 0.01, 0, length/2 - 0.2]}
|
||||
rotation={[0, Math.PI/2, 0]}
|
||||
fontSize={0.06}
|
||||
color="#334155"
|
||||
fontWeight="bold"
|
||||
anchorX="right"
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
// The Moving Block on top of the actuator
|
||||
const IndustrialSlider = ({ width = 0.16, length = 0.18, height = 0.03 }) => {
|
||||
return (
|
||||
<group position={[0, 0.06, 0]}>
|
||||
<RoundedBox args={[width, height, length]} radius={0.005} smoothness={2}>
|
||||
<meshStandardMaterial color="#f1f5f9" metalness={0.4} roughness={0.5} />
|
||||
</RoundedBox>
|
||||
{/* Mounting Holes (Visual) */}
|
||||
{[-1, 1].map(x => [-1, 1].map(z => (
|
||||
<mesh key={`${x}-${z}`} position={[x * 0.06, height/2 + 0.001, z * 0.07]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<circleGeometry args={[0.008, 16]} />
|
||||
<meshStandardMaterial color="#94a3b8" />
|
||||
</mesh>
|
||||
)))}
|
||||
</group>
|
||||
)
|
||||
}
|
||||
|
||||
// -- MAIN ROBOT ASSEMBLY --
|
||||
|
||||
const Robot = ({ target }: { target: RobotTarget }) => {
|
||||
const bridgeGroup = useRef<THREE.Group>(null);
|
||||
const carriageGroup = useRef<THREE.Group>(null);
|
||||
const zAxisGroup = useRef<THREE.Group>(null);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (bridgeGroup.current) {
|
||||
// Y-Axis Movement
|
||||
bridgeGroup.current.position.z = THREE.MathUtils.lerp(bridgeGroup.current.position.z, target.y, delta * 3);
|
||||
}
|
||||
if (carriageGroup.current) {
|
||||
// X-Axis Movement
|
||||
carriageGroup.current.position.x = THREE.MathUtils.lerp(carriageGroup.current.position.x, target.x, delta * 3);
|
||||
}
|
||||
if (zAxisGroup.current) {
|
||||
// Z-Axis Movement
|
||||
zAxisGroup.current.position.y = THREE.MathUtils.lerp(zAxisGroup.current.position.y, target.z, delta * 3);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group position={[0, 2.0, 0]}>
|
||||
|
||||
{/* --- Y-AXIS (Left & Right Fixed Actuators) --- */}
|
||||
<group>
|
||||
<group position={[-1.8, 0, 0]}>
|
||||
<IndustrialActuatorRail length={3.8} label="MISUMI-Y1" />
|
||||
</group>
|
||||
<group position={[1.8, 0, 0]}>
|
||||
<IndustrialActuatorRail length={3.8} label="MISUMI-Y2" />
|
||||
</group>
|
||||
</group>
|
||||
|
||||
{/* --- MOVING BRIDGE (Y-AXIS CARRIAGE + X-AXIS ACTUATOR) --- */}
|
||||
<group ref={bridgeGroup}>
|
||||
|
||||
{/* Y-Sliders (Connecting Bridge to Y-Rails) */}
|
||||
<group position={[-1.8, 0, 0]}> <IndustrialSlider /> </group>
|
||||
<group position={[1.8, 0, 0]}> <IndustrialSlider /> </group>
|
||||
|
||||
{/* Bridge Beam Structure */}
|
||||
<mesh position={[0, 0.08, 0]}>
|
||||
<boxGeometry args={[4.2, 0.05, 0.25]} />
|
||||
<meshStandardMaterial color="#cbd5e1" metalness={0.6} roughness={0.4} />
|
||||
</mesh>
|
||||
|
||||
{/* X-AXIS ACTUATOR (Mounted on top of Bridge) */}
|
||||
<group position={[0, 0.14, 0]} rotation={[0, Math.PI/2, 0]}>
|
||||
<IndustrialActuatorRail length={3.4} label="MISUMI-X" hasMotor />
|
||||
</group>
|
||||
|
||||
{/* --- MOVING CARRIAGE (X-AXIS SLIDER + Z-AXIS) --- */}
|
||||
<group ref={carriageGroup}>
|
||||
|
||||
{/* X-Slider */}
|
||||
<group position={[0, 0.14, 0]} rotation={[0, Math.PI/2, 0]}>
|
||||
<IndustrialSlider />
|
||||
</group>
|
||||
|
||||
{/* --- Z-AXIS ACTUATOR (Vertical) --- */}
|
||||
<group position={[0, 0.2, 0.12]}>
|
||||
{/* Z-Actuator Body (Fixed to X-Slider) */}
|
||||
<group position={[0, 0.4, 0]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<IndustrialActuatorRail length={0.8} label="MISUMI-Z" hasMotor={false} />
|
||||
{/* Z-Motor on Top */}
|
||||
<group position={[0, 0, 0.4 + 0.15]} rotation={[0, Math.PI, 0]}>
|
||||
<mesh position={[0, 0, -0.05]} rotation={[Math.PI/2, 0, 0]}>
|
||||
<boxGeometry args={[0.1, 0.1, 0.12]} />
|
||||
<meshStandardMaterial color="#0f172a" />
|
||||
</mesh>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
{/* MOVING Z-HEAD (The Slider of Z-Axis) */}
|
||||
<group ref={zAxisGroup}>
|
||||
<group position={[0, 0, 0.08]}>
|
||||
{/* Connection Plate */}
|
||||
<mesh position={[0, 0, -0.04]}>
|
||||
<boxGeometry args={[0.12, 0.15, 0.02]} />
|
||||
<meshStandardMaterial color="#64748b" />
|
||||
</mesh>
|
||||
|
||||
{/* PICKER MECHANISM */}
|
||||
<group position={[0, -0.2, 0]}>
|
||||
<mesh position={[0, 0, 0]}>
|
||||
<boxGeometry args={[0.3, 0.05, 0.1]} />
|
||||
<meshStandardMaterial color="#334155" />
|
||||
</mesh>
|
||||
{/* Fingers */}
|
||||
<mesh position={[-0.12, -0.15, 0]}>
|
||||
<boxGeometry args={[0.02, 0.3, 0.05]} />
|
||||
<meshStandardMaterial color="#94a3b8" metalness={0.8} />
|
||||
</mesh>
|
||||
<mesh position={[0.12, -0.15, 0]}>
|
||||
<boxGeometry args={[0.02, 0.3, 0.05]} />
|
||||
<meshStandardMaterial color="#94a3b8" metalness={0.8} />
|
||||
</mesh>
|
||||
|
||||
{/* PCB STRIP */}
|
||||
<group position={[0, -0.35, 0]}>
|
||||
<mesh>
|
||||
<boxGeometry args={[0.28, 0.6, 0.02]} />
|
||||
<meshStandardMaterial color="#166534" roughness={0.3} />
|
||||
</mesh>
|
||||
<mesh position={[0, -0.28, 0.011]}>
|
||||
<planeGeometry args={[0.24, 0.04]} />
|
||||
<meshStandardMaterial color="#fbbf24" metalness={1.0} roughness={0.1} />
|
||||
</mesh>
|
||||
<mesh position={[0, 0, 0.011]}>
|
||||
<planeGeometry args={[0.15, 0.15]} />
|
||||
<meshStandardMaterial color="#0f172a" />
|
||||
</mesh>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
export const Machine3D: React.FC<Machine3DProps> = ({ target, ioState, doorStates }) => {
|
||||
return (
|
||||
<Canvas shadows className="w-full h-full bg-slate-900">
|
||||
<PerspectiveCamera makeDefault position={[5, 5, 6]} fov={45} />
|
||||
<OrbitControls
|
||||
maxPolarAngle={Math.PI / 2 - 0.05}
|
||||
minDistance={3}
|
||||
maxDistance={12}
|
||||
/>
|
||||
<Environment preset="city" />
|
||||
|
||||
<ambientLight intensity={0.4} />
|
||||
<pointLight position={[10, 10, 10]} intensity={1} castShadow />
|
||||
<pointLight position={[-10, 5, -10]} intensity={0.5} />
|
||||
|
||||
<pointLight position={[0, 2, 0]} intensity={0.2} color="#bae6fd" distance={5} />
|
||||
|
||||
<MachineFrame doorStates={doorStates} />
|
||||
<TowerLamp ioState={ioState} />
|
||||
<Robot target={target} />
|
||||
</Canvas>
|
||||
);
|
||||
};
|
||||
67
frontend/components/MotionPanel.tsx
Normal file
67
frontend/components/MotionPanel.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Move } from 'lucide-react';
|
||||
import { RobotTarget, AxisPosition } from '../types';
|
||||
import { PanelHeader } from './common/PanelHeader';
|
||||
import { TechButton } from './common/TechButton';
|
||||
|
||||
const MOCK_POSITIONS: AxisPosition[] = [
|
||||
{ id: 'p1', name: 'Home Position', axis: 'X', value: 0, speed: 100, acc: 100, dec: 100 },
|
||||
{ id: 'p2', name: 'Pick Pos A', axis: 'X', value: -1.5, speed: 500, acc: 200, dec: 200 },
|
||||
{ id: 'p3', name: 'Place Pos B', axis: 'X', value: 1.5, speed: 500, acc: 200, dec: 200 },
|
||||
{ id: 'p4', name: 'Scan Index', axis: 'X', value: 0.5, speed: 300, acc: 100, dec: 100 },
|
||||
{ id: 'py1', name: 'Rear Limit', axis: 'Y', value: -1.5, speed: 200, acc: 100, dec: 100 },
|
||||
{ id: 'pz1', name: 'Safe Height', axis: 'Z', value: 0, speed: 50, acc: 50, dec: 50 },
|
||||
];
|
||||
|
||||
interface MotionPanelProps {
|
||||
robotTarget: RobotTarget;
|
||||
onMove: (axis: 'X' | 'Y' | 'Z', val: number) => void;
|
||||
}
|
||||
|
||||
export const MotionPanel: React.FC<MotionPanelProps> = ({ robotTarget, onMove }) => {
|
||||
const [axis, setAxis] = useState<'X' | 'Y' | 'Z'>('X');
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<PanelHeader title="Servo Control" icon={Move} />
|
||||
<div className="flex gap-2 mb-4">
|
||||
{['X', 'Y', 'Z'].map(a => (
|
||||
<button
|
||||
key={a}
|
||||
onClick={() => setAxis(a as any)}
|
||||
className={`flex-1 py-2 font-tech font-bold text-lg border-b-2 transition-all ${axis === a ? 'text-neon-blue border-neon-blue bg-neon-blue/10' : 'text-slate-500 border-slate-700 hover:text-slate-300'}`}
|
||||
>
|
||||
{a}-AXIS
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="bg-black/40 p-4 rounded border border-white/10 mb-4 text-center">
|
||||
<div className="text-[10px] text-slate-400 uppercase tracking-widest mb-1">Current Position</div>
|
||||
<div className="font-mono text-3xl text-neon-blue text-shadow-glow-blue">
|
||||
{robotTarget[axis.toLowerCase() as 'x' | 'y' | 'z'].toFixed(3)}
|
||||
<span className="text-sm text-slate-500 ml-2">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 overflow-y-auto flex-1 pr-2 custom-scrollbar">
|
||||
{MOCK_POSITIONS.filter(p => p.axis === axis).map(p => (
|
||||
<div key={p.id} className="flex items-center justify-between p-3 bg-white/5 border border-white/5 hover:border-neon-blue/50 transition-all group">
|
||||
<span className="text-xs font-bold text-slate-300 group-hover:text-white">{p.name}</span>
|
||||
<button
|
||||
onClick={() => onMove(axis, p.value)}
|
||||
className="px-3 py-1 bg-slate-800 hover:bg-neon-blue hover:text-black text-xs font-mono transition-colors"
|
||||
>
|
||||
GO {p.value}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 mt-4">
|
||||
<TechButton className="flex-1 font-mono text-lg" onClick={() => onMove(axis, robotTarget[axis.toLowerCase() as 'x' | 'y' | 'z'] - 0.1)}>-</TechButton>
|
||||
<TechButton className="flex-1 font-mono text-lg" onClick={() => onMove(axis, robotTarget[axis.toLowerCase() as 'x' | 'y' | 'z'] + 0.1)}>+</TechButton>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
40
frontend/components/RecipePanel.tsx
Normal file
40
frontend/components/RecipePanel.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { FileText, CheckCircle2, Layers } from 'lucide-react';
|
||||
import { Recipe } from '../types';
|
||||
import { PanelHeader } from './common/PanelHeader';
|
||||
import { TechButton } from './common/TechButton';
|
||||
|
||||
interface RecipePanelProps {
|
||||
recipes: Recipe[];
|
||||
currentRecipe: Recipe;
|
||||
onSelectRecipe: (r: Recipe) => void;
|
||||
}
|
||||
|
||||
export const RecipePanel: React.FC<RecipePanelProps> = ({ recipes, currentRecipe, onSelectRecipe }) => (
|
||||
<div className="h-full flex flex-col">
|
||||
<PanelHeader title="Recipe Select" icon={FileText} />
|
||||
<div className="space-y-2 flex-1 overflow-y-auto custom-scrollbar pr-2">
|
||||
{recipes.map(r => (
|
||||
<div
|
||||
key={r.id}
|
||||
onClick={() => onSelectRecipe(r)}
|
||||
className={`
|
||||
p-3 cursor-pointer flex justify-between items-center transition-all border-l-4
|
||||
${currentRecipe.id === r.id
|
||||
? 'bg-white/10 border-neon-blue text-white shadow-[inset_0_0_20px_rgba(0,243,255,0.1)]'
|
||||
: 'bg-black/20 border-transparent text-slate-500 hover:bg-white/5 hover:text-slate-300'}
|
||||
`}
|
||||
>
|
||||
<div>
|
||||
<div className="font-tech font-bold text-sm tracking-wide">{r.name}</div>
|
||||
<div className="text-[10px] font-mono opacity-60">{r.lastModified}</div>
|
||||
</div>
|
||||
{currentRecipe.id === r.id && <CheckCircle2 className="w-4 h-4 text-neon-blue" />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<TechButton className="w-full mt-4 flex items-center justify-center gap-2">
|
||||
<Layers className="w-4 h-4" /> New Recipe
|
||||
</TechButton>
|
||||
</div>
|
||||
);
|
||||
154
frontend/components/SettingsModal.tsx
Normal file
154
frontend/components/SettingsModal.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
import React from 'react';
|
||||
import { Settings, RotateCw, ChevronDown, ChevronRight } from 'lucide-react';
|
||||
import { ConfigItem } from '../types';
|
||||
|
||||
interface SettingsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
config: ConfigItem[] | null;
|
||||
isRefreshing: boolean;
|
||||
onSave: (config: ConfigItem[]) => void;
|
||||
}
|
||||
|
||||
const TechButton = ({ children, onClick, active = false, variant = 'blue', className = '' }: any) => {
|
||||
const colors: any = {
|
||||
blue: 'from-blue-600 to-cyan-600 hover:shadow-glow-blue border-cyan-400/30',
|
||||
red: 'from-red-600 to-pink-600 hover:shadow-glow-red border-red-400/30',
|
||||
amber: 'from-amber-500 to-orange-600 hover:shadow-orange-500/50 border-orange-400/30',
|
||||
green: 'from-emerald-500 to-green-600 hover:shadow-green-500/50 border-green-400/30'
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`
|
||||
relative px-4 py-2 font-tech font-bold tracking-wider uppercase transition-all duration-300
|
||||
clip-tech border-b-2 border-r-2
|
||||
${active ? `bg-gradient-to-r ${colors[variant]} text-white` : 'bg-slate-800/50 text-slate-400 hover:text-white hover:bg-slate-700/50 border-slate-600'}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{active && <div className="absolute inset-0 bg-white/20 animate-pulse pointer-events-none"></div>}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsModal: React.FC<SettingsModalProps> = ({ isOpen, onClose, config, isRefreshing, onSave }) => {
|
||||
const [localConfig, setLocalConfig] = React.useState<ConfigItem[]>([]);
|
||||
const [expandedGroups, setExpandedGroups] = React.useState<Set<string>>(new Set());
|
||||
|
||||
React.useEffect(() => {
|
||||
if (config) {
|
||||
setLocalConfig(config);
|
||||
// Auto-expand all groups initially
|
||||
const groups = new Set(config.map(c => c.Group));
|
||||
setExpandedGroups(groups);
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
const handleChange = (idx: number, newValue: string) => {
|
||||
setLocalConfig(prev => {
|
||||
const next = [...prev];
|
||||
next[idx] = { ...next[idx], Value: newValue };
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const toggleGroup = (group: string) => {
|
||||
setExpandedGroups(prev => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(group)) next.delete(group);
|
||||
else next.add(group);
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
// Group items by category
|
||||
const groupedConfig = React.useMemo(() => {
|
||||
const groups: { [key: string]: { item: ConfigItem, originalIdx: number }[] } = {};
|
||||
localConfig.forEach((item, idx) => {
|
||||
if (!groups[item.Group]) groups[item.Group] = [];
|
||||
groups[item.Group].push({ item, originalIdx: idx });
|
||||
});
|
||||
return groups;
|
||||
}, [localConfig]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||
<div className="w-[900px] glass-holo p-8 border border-neon-blue shadow-glow-blue relative flex flex-col max-h-[90vh]">
|
||||
<button onClick={onClose} className="absolute top-4 right-4 text-slate-400 hover:text-white">✕</button>
|
||||
<h2 className="text-2xl font-tech font-bold text-neon-blue mb-8 border-b border-white/10 pb-4 flex items-center gap-3 flex-none">
|
||||
<Settings className="animate-spin-slow" /> SYSTEM CONFIGURATION
|
||||
</h2>
|
||||
|
||||
{isRefreshing ? (
|
||||
<div className="h-64 flex flex-col items-center justify-center gap-4 animate-pulse flex-1">
|
||||
<RotateCw className="w-12 h-12 text-neon-blue animate-spin" />
|
||||
<div className="text-xl font-tech text-neon-blue tracking-widest">FETCHING CONFIGURATION...</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 overflow-y-auto custom-scrollbar pr-2 mb-8">
|
||||
<div className="space-y-6">
|
||||
{Object.entries(groupedConfig).map(([groupName, items]) => (
|
||||
<div key={groupName} className="border border-white/10 bg-black/20">
|
||||
<button
|
||||
onClick={() => toggleGroup(groupName)}
|
||||
className="w-full flex items-center gap-2 p-3 bg-white/5 hover:bg-white/10 transition-colors text-left"
|
||||
>
|
||||
{expandedGroups.has(groupName) ? <ChevronDown className="w-4 h-4 text-neon-blue" /> : <ChevronRight className="w-4 h-4 text-slate-400" />}
|
||||
<span className="font-tech font-bold text-lg text-white tracking-wider">{groupName}</span>
|
||||
<span className="text-xs text-slate-500 ml-auto">{items.length} ITEMS</span>
|
||||
</button>
|
||||
|
||||
{expandedGroups.has(groupName) && (
|
||||
<div className="p-4 space-y-4">
|
||||
{items.map(({ item, originalIdx }) => (
|
||||
<div key={originalIdx} className="grid grid-cols-[250px_1fr] gap-6 items-start group">
|
||||
<div>
|
||||
<div className="text-sm font-bold text-neon-blue mb-1">{item.Key}</div>
|
||||
<div className="text-xs text-slate-400 leading-tight">{item.Description}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{item.Type === 'Boolean' ? (
|
||||
<div className="flex items-center gap-3 h-full">
|
||||
<button
|
||||
onClick={() => handleChange(originalIdx, item.Value === 'true' ? 'false' : 'true')}
|
||||
className={`w-12 h-6 rounded-full p-1 transition-colors ${item.Value === 'true' ? 'bg-neon-green' : 'bg-slate-700'}`}
|
||||
>
|
||||
<div className={`w-4 h-4 rounded-full bg-white shadow transition-transform ${item.Value === 'true' ? 'translate-x-6' : 'translate-x-0'}`} />
|
||||
</button>
|
||||
<span className={`font-mono text-sm font-bold ${item.Value === 'true' ? 'text-neon-green' : 'text-slate-400'}`}>
|
||||
{item.Value.toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<input
|
||||
type={item.Type === 'Number' ? 'number' : 'text'}
|
||||
value={item.Value}
|
||||
onChange={(e) => handleChange(originalIdx, e.target.value)}
|
||||
className="w-full bg-black/50 border border-slate-700 text-white font-mono text-sm px-3 py-2 focus:border-neon-blue focus:outline-none transition-colors hover:border-slate-500"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end gap-4 flex-none pt-4 border-t border-white/10">
|
||||
<TechButton onClick={onClose}>CANCEL</TechButton>
|
||||
<TechButton variant="blue" active onClick={() => { onSave(localConfig); onClose(); }}>SAVE CONFIG</TechButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
29
frontend/components/common/CyberPanel.tsx
Normal file
29
frontend/components/common/CyberPanel.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
interface CyberPanelProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const CyberPanel: React.FC<CyberPanelProps> = ({ children, className = "" }) => (
|
||||
<div className={`glass-holo p-1 relative group ${className}`}>
|
||||
{/* Decorative Corners */}
|
||||
<svg className="absolute top-0 left-0 w-6 h-6 text-neon-blue opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M2 22V2H22" />
|
||||
</svg>
|
||||
<svg className="absolute top-0 right-0 w-6 h-6 text-neon-blue opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M22 22V2H2" />
|
||||
</svg>
|
||||
<svg className="absolute bottom-0 left-0 w-6 h-6 text-neon-blue opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M2 2V22H22" />
|
||||
</svg>
|
||||
<svg className="absolute bottom-0 right-0 w-6 h-6 text-neon-blue opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M22 2V22H2" />
|
||||
</svg>
|
||||
|
||||
{/* Inner Content */}
|
||||
<div className="bg-slate-950/40 backdrop-blur-md h-full w-full p-4 relative z-10 clip-tech border border-white/5">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
19
frontend/components/common/PanelHeader.tsx
Normal file
19
frontend/components/common/PanelHeader.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
interface PanelHeaderProps {
|
||||
title: string;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
export const PanelHeader: React.FC<PanelHeaderProps> = ({ title, icon: Icon }) => (
|
||||
<div className="flex items-center gap-3 mb-6 border-b border-white/10 pb-2">
|
||||
<div className="text-neon-blue animate-pulse">
|
||||
<Icon className="w-5 h-5" />
|
||||
</div>
|
||||
<h2 className="text-lg font-tech font-bold text-white tracking-[0.1em] uppercase text-shadow-glow-blue">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="flex-1 h-px bg-gradient-to-r from-neon-blue/50 to-transparent"></div>
|
||||
</div>
|
||||
);
|
||||
39
frontend/components/common/TechButton.tsx
Normal file
39
frontend/components/common/TechButton.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
|
||||
interface TechButtonProps {
|
||||
children?: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
active?: boolean;
|
||||
variant?: 'blue' | 'red' | 'amber' | 'green';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TechButton: React.FC<TechButtonProps> = ({
|
||||
children,
|
||||
onClick,
|
||||
active = false,
|
||||
variant = 'blue',
|
||||
className = ''
|
||||
}) => {
|
||||
const colors = {
|
||||
blue: 'from-blue-600 to-cyan-600 hover:shadow-glow-blue border-cyan-400/30',
|
||||
red: 'from-red-600 to-pink-600 hover:shadow-glow-red border-red-400/30',
|
||||
amber: 'from-amber-500 to-orange-600 hover:shadow-orange-500/50 border-orange-400/30',
|
||||
green: 'from-emerald-500 to-green-600 hover:shadow-green-500/50 border-green-400/30'
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`
|
||||
relative px-4 py-2 font-tech font-bold tracking-wider uppercase transition-all duration-300
|
||||
clip-tech border-b-2 border-r-2
|
||||
${active ? `bg-gradient-to-r ${colors[variant]} text-white` : 'bg-slate-800/50 text-slate-400 hover:text-white hover:bg-slate-700/50 border-slate-600'}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{active && <div className="absolute inset-0 bg-white/20 animate-pulse pointer-events-none"></div>}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
108
frontend/index.css
Normal file
108
frontend/index.css
Normal file
@@ -0,0 +1,108 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 243, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 243, 255, 0.6);
|
||||
}
|
||||
|
||||
/* Holographic Glass */
|
||||
.glass-holo {
|
||||
background: linear-gradient(135deg, rgba(10, 15, 30, 0.7) 0%, rgba(20, 30, 50, 0.5) 100%);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(0, 243, 255, 0.1);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glass-holo::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 243, 255, 0.5), transparent);
|
||||
}
|
||||
|
||||
/* Active Element */
|
||||
.glass-active {
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid rgba(0, 243, 255, 0.4);
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Text Glows */
|
||||
.text-glow-blue {
|
||||
color: #00f3ff;
|
||||
text-shadow: 0 0 8px rgba(0, 243, 255, 0.6);
|
||||
}
|
||||
|
||||
.text-glow-purple {
|
||||
color: #bc13fe;
|
||||
text-shadow: 0 0 8px rgba(188, 19, 254, 0.6);
|
||||
}
|
||||
|
||||
.text-glow-red {
|
||||
color: #ff0055;
|
||||
text-shadow: 0 0 8px rgba(255, 0, 85, 0.6);
|
||||
}
|
||||
|
||||
.text-glow-green {
|
||||
color: #0aff00;
|
||||
text-shadow: 0 0 8px rgba(10, 255, 0, 0.6);
|
||||
}
|
||||
|
||||
/* Grid Background */
|
||||
.grid-bg {
|
||||
background-size: 50px 50px;
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(0, 243, 255, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(0, 243, 255, 0.05) 1px, transparent 1px);
|
||||
mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
|
||||
}
|
||||
|
||||
/* CRT Scanline Effect */
|
||||
.scanlines {
|
||||
background: linear-gradient(to bottom,
|
||||
rgba(255, 255, 255, 0),
|
||||
rgba(255, 255, 255, 0) 50%,
|
||||
rgba(0, 0, 0, 0.2) 50%,
|
||||
rgba(0, 0, 0, 0.2));
|
||||
background-size: 100% 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Tech Clip Path */
|
||||
.clip-tech {
|
||||
clip-path: polygon(0 0,
|
||||
100% 0,
|
||||
100% calc(100% - 10px),
|
||||
calc(100% - 10px) 100%,
|
||||
0 100%);
|
||||
}
|
||||
|
||||
.clip-tech-inv {
|
||||
clip-path: polygon(10px 0,
|
||||
100% 0,
|
||||
100% 100%,
|
||||
0 100%,
|
||||
0 10px);
|
||||
}
|
||||
197
frontend/index.html
Normal file
197
frontend/index.html
Normal file
@@ -0,0 +1,197 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Industrial HMI - Holographic</title>
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500&family=Rajdhani:wght@500;600;700&display=swap" rel="stylesheet">
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"react-dom/client": "https://esm.sh/react-dom@18.2.0/client",
|
||||
"react-dom": "https://esm.sh/react-dom@18.2.0",
|
||||
"react/jsx-runtime": "https://esm.sh/react@18.2.0/jsx-runtime",
|
||||
"react": "https://esm.sh/react@18.2.0",
|
||||
"three": "https://esm.sh/three@0.160.0",
|
||||
"@react-three/fiber": "https://esm.sh/@react-three/fiber@8.15.12?external=react,react-dom,three",
|
||||
"@react-three/drei": "https://esm.sh/@react-three/drei@9.96.1?external=react,react-dom,three,@react-three/fiber",
|
||||
"lucide-react": "https://esm.sh/lucide-react@0.303.0?external=react",
|
||||
"clsx": "https://esm.sh/clsx@2.1.0",
|
||||
"tailwind-merge": "https://esm.sh/tailwind-merge@2.2.0",
|
||||
"path": "https://aistudiocdn.com/path@^0.12.7",
|
||||
"react/": "https://aistudiocdn.com/react@^19.2.0/",
|
||||
"react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
|
||||
"vite": "https://aistudiocdn.com/vite@^7.2.4",
|
||||
"@vitejs/plugin-react": "https://aistudiocdn.com/@vitejs/plugin-react@^5.1.1",
|
||||
"url": "https://aistudiocdn.com/url@^0.11.4"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
mono: ['Rajdhani', 'monospace'],
|
||||
tech: ['Rajdhani', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
slate: {
|
||||
850: '#1e293b',
|
||||
900: '#0f172a',
|
||||
950: '#020617',
|
||||
},
|
||||
neon: {
|
||||
blue: '#00f3ff',
|
||||
purple: '#bc13fe',
|
||||
pink: '#ff0099',
|
||||
green: '#0aff00',
|
||||
yellow: '#ffe600',
|
||||
}
|
||||
},
|
||||
boxShadow: {
|
||||
'glow-blue': '0 0 20px rgba(0, 243, 255, 0.3), inset 0 0 10px rgba(0, 243, 255, 0.1)',
|
||||
'glow-purple': '0 0 20px rgba(188, 19, 254, 0.4), inset 0 0 10px rgba(188, 19, 254, 0.1)',
|
||||
'glow-red': '0 0 30px rgba(255, 0, 0, 0.5)',
|
||||
'hologram': '0 0 15px rgba(6, 182, 212, 0.15), inset 0 0 20px rgba(6, 182, 212, 0.05)',
|
||||
},
|
||||
animation: {
|
||||
'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
'glow': 'glow 2s ease-in-out infinite alternate',
|
||||
'gradient': 'gradient 15s ease infinite',
|
||||
'scan': 'scan 4s linear infinite',
|
||||
'spin-slow': 'spin 10s linear infinite',
|
||||
},
|
||||
keyframes: {
|
||||
glow: {
|
||||
'0%': { boxShadow: '0 0 5px rgba(0, 243, 255, 0.2)' },
|
||||
'100%': { boxShadow: '0 0 20px rgba(0, 243, 255, 0.6), 0 0 10px rgba(0, 243, 255, 0.4)' },
|
||||
},
|
||||
gradient: {
|
||||
'0%': { backgroundPosition: '0% 50%' },
|
||||
'50%': { backgroundPosition: '100% 50%' },
|
||||
'100%': { backgroundPosition: '0% 50%' },
|
||||
},
|
||||
scan: {
|
||||
'0%': { transform: 'translateY(-100%)' },
|
||||
'100%': { transform: 'translateY(100%)' },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 243, 255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 243, 255, 0.6);
|
||||
}
|
||||
|
||||
/* Holographic Glass */
|
||||
.glass-holo {
|
||||
background: linear-gradient(135deg, rgba(10, 15, 30, 0.7) 0%, rgba(20, 30, 50, 0.5) 100%);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(0, 243, 255, 0.1);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glass-holo::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 243, 255, 0.5), transparent);
|
||||
}
|
||||
|
||||
/* Active Element */
|
||||
.glass-active {
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid rgba(0, 243, 255, 0.4);
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Text Glows */
|
||||
.text-glow-blue {
|
||||
color: #00f3ff;
|
||||
text-shadow: 0 0 8px rgba(0, 243, 255, 0.6);
|
||||
}
|
||||
.text-glow-purple {
|
||||
color: #bc13fe;
|
||||
text-shadow: 0 0 8px rgba(188, 19, 254, 0.6);
|
||||
}
|
||||
.text-glow-red {
|
||||
color: #ff0055;
|
||||
text-shadow: 0 0 8px rgba(255, 0, 85, 0.6);
|
||||
}
|
||||
.text-glow-green {
|
||||
color: #0aff00;
|
||||
text-shadow: 0 0 8px rgba(10, 255, 0, 0.6);
|
||||
}
|
||||
|
||||
/* Grid Background */
|
||||
.grid-bg {
|
||||
background-size: 50px 50px;
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(0, 243, 255, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(0, 243, 255, 0.05) 1px, transparent 1px);
|
||||
mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
|
||||
}
|
||||
|
||||
/* CRT Scanline Effect */
|
||||
.scanlines {
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255,255,255,0),
|
||||
rgba(255,255,255,0) 50%,
|
||||
rgba(0,0,0,0.2) 50%,
|
||||
rgba(0,0,0,0.2)
|
||||
);
|
||||
background-size: 100% 4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Tech Clip Path */
|
||||
.clip-tech {
|
||||
clip-path: polygon(
|
||||
0 0,
|
||||
100% 0,
|
||||
100% calc(100% - 10px),
|
||||
calc(100% - 10px) 100%,
|
||||
0 100%
|
||||
);
|
||||
}
|
||||
.clip-tech-inv {
|
||||
clip-path: polygon(
|
||||
10px 0,
|
||||
100% 0,
|
||||
100% 100%,
|
||||
0 100%,
|
||||
0 10px
|
||||
);
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/index.css">
|
||||
</head>
|
||||
<body class="bg-black text-slate-50 overflow-hidden font-sans antialiased selection:bg-neon-blue/30">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
22
frontend/index.tsx
Normal file
22
frontend/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import '@fontsource/inter/300.css';
|
||||
import '@fontsource/inter/400.css';
|
||||
import '@fontsource/inter/500.css';
|
||||
import '@fontsource/rajdhani/500.css';
|
||||
import '@fontsource/rajdhani/600.css';
|
||||
import '@fontsource/rajdhani/700.css';
|
||||
import './index.css';
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
throw new Error("Could not find root element to mount to");
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
7
frontend/metadata.json
Normal file
7
frontend/metadata.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Industrial HMI 3D",
|
||||
"description": "A Next-Gen Human Machine Interface with 3D simulation, motion control, and I/O monitoring.",
|
||||
"requestFramePermissions": [
|
||||
"camera"
|
||||
]
|
||||
}
|
||||
3502
frontend/package-lock.json
generated
Normal file
3502
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
frontend/package.json
Normal file
34
frontend/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "industrial-hmi-3d",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^5.2.8",
|
||||
"@fontsource/rajdhani": "^5.2.7",
|
||||
"@react-three/drei": "^9.96.1",
|
||||
"@react-three/fiber": "^8.15.12",
|
||||
"clsx": "^2.1.0",
|
||||
"lucide-react": "^0.303.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"three": "^0.160.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/three": "^0.160.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.32",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.10"
|
||||
}
|
||||
}
|
||||
6
frontend/postcss.config.js
Normal file
6
frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
3
frontend/run.bat
Normal file
3
frontend/run.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
echo Starting Frontend Dev Server...
|
||||
call npm run dev
|
||||
59
frontend/tailwind.config.js
Normal file
59
frontend/tailwind.config.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
mono: ['Rajdhani', 'monospace'],
|
||||
tech: ['Rajdhani', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
slate: {
|
||||
850: '#1e293b',
|
||||
900: '#0f172a',
|
||||
950: '#020617',
|
||||
},
|
||||
neon: {
|
||||
blue: '#00f3ff',
|
||||
purple: '#bc13fe',
|
||||
pink: '#ff0099',
|
||||
green: '#0aff00',
|
||||
yellow: '#ffe600',
|
||||
}
|
||||
},
|
||||
boxShadow: {
|
||||
'glow-blue': '0 0 20px rgba(0, 243, 255, 0.3), inset 0 0 10px rgba(0, 243, 255, 0.1)',
|
||||
'glow-purple': '0 0 20px rgba(188, 19, 254, 0.4), inset 0 0 10px rgba(188, 19, 254, 0.1)',
|
||||
'glow-red': '0 0 30px rgba(255, 0, 0, 0.5)',
|
||||
'hologram': '0 0 15px rgba(6, 182, 212, 0.15), inset 0 0 20px rgba(6, 182, 212, 0.05)',
|
||||
},
|
||||
animation: {
|
||||
'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
'glow': 'glow 2s ease-in-out infinite alternate',
|
||||
'gradient': 'gradient 15s ease infinite',
|
||||
'scan': 'scan 4s linear infinite',
|
||||
'spin-slow': 'spin 10s linear infinite',
|
||||
},
|
||||
keyframes: {
|
||||
glow: {
|
||||
'0%': { boxShadow: '0 0 5px rgba(0, 243, 255, 0.2)' },
|
||||
'100%': { boxShadow: '0 0 20px rgba(0, 243, 255, 0.6), 0 0 10px rgba(0, 243, 255, 0.4)' },
|
||||
},
|
||||
gradient: {
|
||||
'0%': { backgroundPosition: '0% 50%' },
|
||||
'50%': { backgroundPosition: '100% 50%' },
|
||||
'100%': { backgroundPosition: '0% 50%' },
|
||||
},
|
||||
scan: {
|
||||
'0%': { transform: 'translateY(-100%)' },
|
||||
'100%': { transform: 'translateY(100%)' },
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
22
frontend/tsconfig.json
Normal file
22
frontend/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["vite.config.ts", "node_modules"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
10
frontend/tsconfig.node.json
Normal file
10
frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
73
frontend/types.ts
Normal file
73
frontend/types.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
export enum SystemState {
|
||||
IDLE = 'IDLE',
|
||||
RUNNING = 'RUNNING',
|
||||
ERROR = 'ERROR',
|
||||
PAUSED = 'PAUSED',
|
||||
}
|
||||
|
||||
export interface AxisPosition {
|
||||
id: string;
|
||||
name: string;
|
||||
axis: 'X' | 'Y' | 'Z';
|
||||
value: number;
|
||||
speed: number;
|
||||
acc: number;
|
||||
dec: number;
|
||||
}
|
||||
|
||||
export interface Recipe {
|
||||
id: string;
|
||||
name: string;
|
||||
lastModified: string;
|
||||
}
|
||||
|
||||
export interface IOPoint {
|
||||
id: number;
|
||||
name: string;
|
||||
type: 'input' | 'output';
|
||||
state: boolean;
|
||||
}
|
||||
|
||||
export interface LogEntry {
|
||||
id: number;
|
||||
timestamp: string;
|
||||
message: string;
|
||||
type: 'info' | 'warning' | 'error';
|
||||
}
|
||||
|
||||
export interface RobotTarget {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}
|
||||
|
||||
// WebView2 Native Bridge Types
|
||||
export interface ConfigItem {
|
||||
Key: string;
|
||||
Value: string;
|
||||
Group: string;
|
||||
Type: 'String' | 'Number' | 'Boolean';
|
||||
Description: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
chrome: {
|
||||
webview: {
|
||||
hostObjects: {
|
||||
machine: {
|
||||
MoveAxis(axis: string, value: number): Promise<void>;
|
||||
SetIO(id: number, isInput: boolean, state: boolean): Promise<void>;
|
||||
SystemControl(command: string): Promise<void>;
|
||||
LoadRecipe(recipeId: string): Promise<void>;
|
||||
GetConfig(): Promise<string>;
|
||||
SaveConfig(configJson: string): Promise<void>;
|
||||
}
|
||||
};
|
||||
addEventListener(type: string, listener: (event: any) => void): void;
|
||||
removeEventListener(type: string, listener: (event: any) => void): void;
|
||||
postMessage(message: any): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
frontend/vite.config.ts
Normal file
20
frontend/vite.config.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
emptyOutDir: true,
|
||||
}
|
||||
});
|
||||
40
walkthrough.md
Normal file
40
walkthrough.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Project Investigation Report
|
||||
|
||||
## Overview
|
||||
The project is a **Hybrid Industrial HMI (Human-Machine Interface)** application. It combines a **WinForms (.NET)** backend acting as a host and PLC simulator with a modern **React** frontend for the UI and 3D visualization.
|
||||
|
||||
## Architecture
|
||||
- **Type:** Desktop Application (Hybrid)
|
||||
- **Host:** Windows Forms (WinForms) using `WebView2` control.
|
||||
- **Communication:** Bidirectional bridge between C# and JavaScript.
|
||||
|
||||
## Backend (`/backend`)
|
||||
- **Framework:** .NET Framework 4.8
|
||||
- **Project Type:** Windows Forms (`HMIWeb.sln`)
|
||||
- **Key Files:**
|
||||
- `MainForm.cs`: Main entry point. Initializes `WebView2`, sets up the virtual host (`http://hmi.local`), and runs the simulation loop (50ms tick).
|
||||
- `MachineBridge.cs`: Exposed to JavaScript via `AddHostObjectToScript`. Allows the frontend to call C# methods (`MoveAxis`, `SetIO`, `SystemControl`).
|
||||
- `Program.cs`: Standard WinForms entry point.
|
||||
|
||||
## Frontend (`/frontend`)
|
||||
- **Framework:** React 18, Vite, TypeScript
|
||||
- **Styling:** Tailwind CSS (Cyberpunk/Neon aesthetic)
|
||||
- **3D Graphics:** Three.js (`@react-three/fiber`, `@react-three/drei`)
|
||||
- **Key Files:**
|
||||
- `App.tsx`: Main application logic. Handles communication with the backend and manages UI state. Includes a **Mock Mode** for browser-based development.
|
||||
- `components/Machine3D.tsx`: Likely handles the 3D visualization of the machine.
|
||||
- **Integration:**
|
||||
- **Receiving Data:** Listens for `message` events from `window.chrome.webview`.
|
||||
- **Sending Commands:** Calls `window.chrome.webview.hostObjects.machine.[MethodName]`.
|
||||
|
||||
## Communication Protocol
|
||||
1. **Backend to Frontend (Status Updates):**
|
||||
- Sent every 50ms via `PostWebMessageAsJson`.
|
||||
- Payload: `{ type: "STATUS_UPDATE", sysState, position, ioState }`.
|
||||
2. **Frontend to Backend (Control):**
|
||||
- Direct method calls via `MachineBridge` proxy.
|
||||
- Methods: `MoveAxis(axis, value)`, `SetIO(id, isInput, state)`, `SystemControl(command)`.
|
||||
|
||||
## Development
|
||||
- **Frontend:** Can be developed independently using `npm run dev`. It will use mock data since `window.chrome.webview` is missing.
|
||||
- **Backend:** Requires Visual Studio to build and run the WinForms app. It expects the frontend build artifacts in `backend/HMIWeb/bin/Debug/wwwroot` (or similar, mapped to `wwwroot`).
|
||||
Reference in New Issue
Block a user