Yess
Some checks failed
Mirror to Gitea / git-sync (push) Has been cancelled

This commit is contained in:
2026-03-28 00:26:55 +02:00
parent ea2d84f5cc
commit ae0994409a
15 changed files with 660 additions and 94 deletions

View File

@@ -0,0 +1,10 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ModuleWeather.WeatherView">
<StackPanel Margin="20" Spacing="10" Background="#2b2b2b">
<Button Content="Обновить" Click="Update"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<TextBlock Name="StatusText" Text="Выберите порт" Foreground="White"/>
<ComboBox Name="PortComboBox" SelectionChanged="OnPortChanged"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,21 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using System.Reflection;
namespace ModuleWeather;
public partial class WeatherView : UserControl
{
public WeatherView() => InitializeComponent();
private void Update(object? sender, RoutedEventArgs e)
{
}
private void OnPortChanged(object sender, SelectionChangedEventArgs e)
{
}
}

View File

@@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using VisionAsist.SDK;
namespace ModuleArduino;
public class ArduinoModule : IModule
{
public string Name => "ArduinoModule";
public string Description => "Модуль для работы с COM-портами и прямой прошивки кода в Arduino";
public void Settings(object[] args) { }
public List<ToolDefinition> GetTools()
{
return new List<ToolDefinition>
{
new ToolDefinition
{
Name = "list_com_ports",
Description = "Получает список всех доступных COM-портов в системе",
ParametersSchema = "{\"type\": \"object\", \"properties\": {}}"
},
new ToolDefinition
{
Name = "send_serial_data",
Description = "Отправляет строку в указанный COM-порт",
ParametersSchema = JsonSerializer.Serialize(new
{
type = "object",
properties = new
{
port = new { type = "string", description = "Имя порта (например, COM3)" },
baudRate = new { type = "integer", description = "Скорость передачи (по умолчанию 9600)", @default = 9600 },
data = new { type = "string", description = "Данные для отправки" }
},
required = new[] { "port", "data" }
})
},
new ToolDefinition
{
Name = "read_serial_data",
Description = "Читает любые доступные данные из указанного COM-порта",
ParametersSchema = JsonSerializer.Serialize(new
{
type = "object",
properties = new
{
port = new { type = "string", description = "Имя порта (например, COM3)" },
baudRate = new { type = "integer", description = "Скорость передачи (по умолчанию 9600)", @default = 9600 },
timeoutMs = new { type = "integer", description = "Максимальное время ожидания данных в миллисекундах (по умолчанию 3000)", @default = 3000 }
},
required = new[] { "port" }
})
},
new ToolDefinition
{
Name = "compile_upload_code",
Description = "Принимает исходный код C++ (Arduino), компилирует его и загружает на плату",
ParametersSchema = JsonSerializer.Serialize(new
{
type = "object",
properties = new
{
code = new { type = "string", description = "Полный текст скетча .ino (включая setup и loop)" },
port = new { type = "string", description = "Имя порта (например, COM3)" },
boardType = new { type = "string", description = "Тип платы (например, arduino:avr:uno)" }
},
required = new[] { "code", "port", "boardType" }
})
}
};
}
public string Execute(string toolName, string argumentsJson)
{
try
{
var doc = JsonDocument.Parse(argumentsJson);
var root = doc.RootElement;
switch (toolName)
{
case "list_com_ports":
var ports = SerialPort.GetPortNames();
return ports.Length > 0 ? string.Join(", ", ports) : "Порты не найдены";
case "send_serial_data":
string portName = root.GetProperty("port").GetString()!;
string data = root.GetProperty("data").GetString()!;
int baud = root.TryGetProperty("baudRate", out var b) ? b.GetInt32() : 9600;
using (var serial = new SerialPort(portName, baud))
{
serial.DtrEnable = true; // Важно для многих плат Arduino
serial.RtsEnable = true;
serial.Open();
serial.WriteLine(data);
Thread.Sleep(100); // Даем время на отправку
serial.Close();
}
return $"Отправлено в {portName}: {data}";
case "read_serial_data":
string rPortName = root.GetProperty("port").GetString()!;
int rBaud = root.TryGetProperty("baudRate", out var rb) ? rb.GetInt32() : 9600;
int timeout = root.TryGetProperty("timeoutMs", out var rt) ? rt.GetInt32() : 3000;
using (var serial = new SerialPort(rPortName, rBaud))
{
serial.DtrEnable = true; // Без этого Arduino может не слать данные
serial.RtsEnable = true;
serial.Open();
// После открытия порта Arduino может перезагрузиться,
// дадим ей немного времени прийти в себя
Thread.Sleep(500);
int waited = 500;
while (serial.BytesToRead == 0 && waited < timeout)
{
Thread.Sleep(100);
waited += 100;
}
if (serial.BytesToRead > 0)
{
string received = serial.ReadExisting();
serial.Close();
return $"Получено из {rPortName}: {received}";
}
serial.Close();
return $"Данные из {rPortName} не поступили за {timeout}мс.";
}
case "compile_upload_code":
string code = root.GetProperty("code").GetString()!;
string p = root.GetProperty("port").GetString()!;
string board = root.GetProperty("boardType").GetString()!;
return HandleCompileAndUpload(code, p, board);
default:
return "Инструмент не найден";
}
}
catch (Exception ex) { return $"Ошибка: {ex.Message}"; }
}
private string HandleCompileAndUpload(string code, string port, string board)
{
string tempDir = Path.Combine(Path.GetTempPath(), "VisionAsist_Arduino_" + Guid.NewGuid().ToString("N"));
string sketchName = "Sketch";
string sketchDir = Path.Combine(tempDir, sketchName);
string sketchPath = Path.Combine(sketchDir, sketchName + ".ino");
string modulePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
string arduinoCliPath = Path.Combine(modulePath, "arduino-cli.exe");
if (!File.Exists(arduinoCliPath)) arduinoCliPath = "arduino-cli";
try
{
Directory.CreateDirectory(sketchDir);
File.WriteAllText(sketchPath, code);
var compileResult = RunCommand(arduinoCliPath, $"compile --fqbn {board} \"{sketchDir}\"");
if (compileResult.ExitCode != 0) return $"Ошибка компиляции:\n{compileResult.Error}";
var uploadResult = RunCommand(arduinoCliPath, $"upload -p {port} --fqbn {board} \"{sketchDir}\"");
if (uploadResult.ExitCode != 0) return $"Ошибка загрузки:\n{uploadResult.Error}";
return "Код успешно скомпилирован и загружен в Arduino!";
}
catch (Exception ex) { return $"Критическая ошибка: {ex.Message}"; }
finally
{
try { if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } catch { }
}
}
private (int ExitCode, string Output, string Error) RunCommand(string cmd, string args)
{
try
{
var process = Process.Start(new ProcessStartInfo
{
FileName = cmd,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
});
string output = process?.StandardOutput.ReadToEnd() ?? "";
string error = process?.StandardError.ReadToEnd() ?? "";
process?.WaitForExit();
return (process?.ExitCode ?? -1, output, error);
}
catch (System.ComponentModel.Win32Exception)
{
return (-1, "", $"Ошибка: Исполняемый файл '{cmd}' не найден в папке модуля или в системе.");
}
}
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>ArduinoModule</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\VisionAsist.SDK\VisionAsist.SDK.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.12" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
<PackageReference Include="System.IO.Ports" Version="9.0.0" />
</ItemGroup>
<Target Name="CopyModuleToCore" AfterTargets="PostBuildEvent">
<ItemGroup>
<!-- Копируем ВСЕ DLL и PDB рекурсивно, чтобы не потерять зависимости NuGet -->
<ModuleFiles Include="$(TargetDir)**\*.dll" />
<ModuleFiles Include="$(TargetDir)**\*.pdb" />
</ItemGroup>
<Message Text="Копирую файлы модуля и все зависимости..." Importance="high" />
<Copy SourceFiles="@(ModuleFiles)"
DestinationFolder="E:\Project\Visual\VisionAsist\VisionAsist\bin\Debug\net10.0\Modules\ArduinoModule"
OverwriteReadOnlyFiles="true" />
</Target>
<ItemGroup>
<Compile Update="MainWindow.axaml.cs">
<DependentUpon>MainWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>