Files
Egor 14ebb04c1c
Some checks failed
Mirror to Gitea / git-sync (push) Has been cancelled
CrossPlatform
2026-03-28 00:29:00 +02:00

218 lines
9.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)!;
// Определяем имя файла в зависимости от ОС
bool isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
string exeName = isWindows ? "arduino-cli.exe" : "arduino-cli";
string arduinoCliPath = Path.Combine(modulePath, exeName);
if (!File.Exists(arduinoCliPath)) arduinoCliPath = exeName;
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}' не найден в папке модуля или в системе.");
}
}
}