그러다가 안드로이드가 나왔고 xml기반의 뷰드로잉이 희안하다고 생각했고
하지만 지금에 와서는 디자이너가 있고 파일로 분리되는 깔끔함은 사실상 PyQt로
디자인도 구린데 이렇게 까지 해야하나.. 이런 생각이 든다.
중간 어디쯤인 프로그램 답게 그냥 코드는 노출되어 상관이 없을것이고
파일을 읽어서 그 안의 내용을 pip install 처리하면 될것이다.
it블로그
요즘에는 라즈베리파이니 오렌지파이니 해서 소형 리눅스 디바이스가 활약하는 일이 잦아졌다.
sudo systemctl daemon-reload
sudo systemctl enable orange-net
sudo systemctl start orange-net
파워쉘이 강력하다는것은 전부터 익히 들어 알고 있었다.
Azure 원격관리도 할수 있고 자동화 스크립트 이런걸로 말이다..
그리고 파워쉘을 사용하기 위해 다음과 같은 배치파일 (myapp.ps1 파일을 실행)
PowerShell.exe -executionpolicy bypass -file "%~dp0myapp.ps1"
pause
를 보통 사용할것이다. 명령프롬프트가 없으려면 다음처럼
PowerShell.exe -windowstyle Hidden -executionpolicy bypass -file "%~dp0myapp.ps1"
그런데 얼마전 일을하다가 파워쉘의 다른 모습을 봤는데..
이 파워쉘이라는것이 닷넷프레임워크 4.x 기반위에서 동작하고
C# 문법과 비슷하게 프로그래밍이 가능하다는 기능이 있다는걸 알게됬다.
물론 완전한 인터렉션용 프로그램은 아닐지라도 자동화처리를 함에 있어
스트립트 라인바이라인 형식의 프로그래밍을 사용할수 있다는걸 알았다.
그래서 그 위상이 한층 높아졌는데..
닷넷프레임워크를 사용할수 있으니 윈폼이라던가 Math, Socket을 사용할수 있다.
결과적으로
복잡한로직은 c# 라이브러리로 만들고
이걸 파워쉘에서 불러서 호출한다던가
Bitmap을 이용한 간단한 이미지를 생성한다던가
예를들어 직원 명단이 있는 csv파일을 불러다가
Bitmap을 루프돌면서 명찰 PNG파일을 만들어서
각 이름 폴더에 저장한다던가 dll파일이 있는데
호출규약이나 구조만 알아낸뒤 이부분만 떼어서 별도로 호출해서
결과를 바로 본다던가..
이런행위들은 이제 별도의 프로그램을 만들지말고 파워쉘로 하면된다.
물론 파이썬도 이런거 할수 있긴하지만 명령창까지 품은
파워쉘이 훨씬 강력하다고 느낀다.
게다가 AI가 판지는 요즈에는 커맨드릿 이라던가 이런거 외우지 않아도
바로바로 질문해서 답을 얻을수 있다.
바이브 AI코딩 + 파워쉘... 장난없다..
게다가 파워쉘로 snaketail을 다음명령으로 대체가능하다.
Get-Content "텍스트파일경로" -Wait -Tail 20
ㅇㅇ
using System;using System.Runtime.InteropServices;namespace GpioTest;/// <summary>/// 네이티브 바인딩/// </summary>public static class NativeBindings{ //라이브러리 파일명 private const string LibGpiod = "libgpiod.so"; //gpiod.h 의 풀업플레그값 public const int GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP = 1 << 8; // so 파일 메서드 시그니처 [DllImport(LibGpiod, EntryPoint = "gpiod_chip_open_by_name")] public static extern IntPtr GpiodChipOpenByName(string name); [DllImport(LibGpiod, EntryPoint = "gpiod_chip_close")] public static extern void GpiodChipClose(IntPtr chip); [DllImport(LibGpiod, EntryPoint = "gpiod_chip_get_line")] public static extern IntPtr GpiodChipGetLine(IntPtr chip, uint offset); [DllImport(LibGpiod, EntryPoint = "gpiod_line_request_input_flags")] public static extern int GpiodLineRequestInputFlags(IntPtr line, string consumer, int flags); [DllImport(LibGpiod, EntryPoint = "gpiod_line_request_input")] public static extern int GpiodLineRequestInput(IntPtr line, string consumer); [DllImport(LibGpiod, EntryPoint = "gpiod_line_get_value")] public static extern int GpiodLineGetValue(IntPtr line); [DllImport(LibGpiod, EntryPoint = "gpiod_line_release")] public static extern void GpiodLineRelease(IntPtr line);}using System;using System.Collections.Generic;using System.Linq;using System.Threading;namespace GpioTest;/// <summary>/// 버튼정보 구조체/// </summary>public class GpioButtonConfig{ public IntPtr LinePtr = IntPtr.Zero; public uint Lineoffset { get; set; } = 0; public string Description { get; set; } = ""; public int LastValue { get; set; } = 1; // 1 (HIGH): 떼어짐 (풀업 가정) public Action<uint>? d_Actor = null;}/// <summary>/// Gpio 모니터/// </summary>public class GpioMonitor: IDisposable{ private IntPtr _chipPtr = IntPtr.Zero; private string ChipName = ""; private List<GpioButtonConfig> _buttonConfigs = new List<GpioButtonConfig>(); // 전체 버튼의 디바운스 타임 private const int DebounceDelayMs = 250; private DateTime _lastPushDebounceTime = DateTime.MinValue; private DateTime _lastPullDebounceTime = DateTime.MinValue; /// <summary> /// 초기화처리 /// </summary> public bool Initialize(List<GpioButtonConfig> a_configs, string a_chipName = "gpiochip1") { // _buttonConfigs.AddRange(a_configs); ChipName = a_chipName; // try { // 1. 칩열기 _chipPtr = NativeBindings.GpiodChipOpenByName(ChipName); if (_chipPtr == IntPtr.Zero) { throw new Exception($"칩 {ChipName} 열기 실패. 권한 문제확인."); } // 2. 등록된 모든 라인 초기화 및 요청 for (int i = 0; i < _buttonConfigs.Count; i++) { GpioButtonConfig one_config = _buttonConfigs[i]; IntPtr linePtr = NativeBindings.GpiodChipGetLine(_chipPtr, one_config.Lineoffset); if (linePtr == IntPtr.Zero) { Console.WriteLine($"경고: 라인 {one_config.Lineoffset} 가져오기 실패. 건너뜀"); continue; } // 3. 라인 요청 (Input + Pull Up) int flags = NativeBindings.GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; int ret = NativeBindings.GpiodLineRequestInputFlags(linePtr, one_config.Description, flags); if (ret < 0) { Console.WriteLine($"경고: 라인 {one_config.Lineoffset} 풀업요청 실패."); } //라인포인터 추가 및 액션 바인딩 one_config.LinePtr = linePtr; one_config.LastValue = NativeBindings.GpiodLineGetValue(linePtr); one_config.d_Actor += (lineno) => System.Console.WriteLine($"눌린 라인번호{lineno}번."); Console.WriteLine($"라인 {one_config.Lineoffset} ({one_config.Description}) 설정 완료."); } // if (!_buttonConfigs.Any()) { throw new Exception("모니터링할 유효한 GPIO라인이 없습니다."); } // return true; } catch (Exception ex) { Console.WriteLine($"\n 초기화 실패: {ex.Message}"); Dispose(); return false; } } /// <summary> /// 모니터링 시작 /// </summary> public void StartMonitoring() { //루핑 while (true) { foreach (var item in _buttonConfigs.Where(c => c.LinePtr != IntPtr.Zero)) { int currentValue = NativeBindings.GpiodLineGetValue(item.LinePtr); // if (currentValue == 0 && item.LastValue == 1) { //버튼 눌림 감지 DateTime nowtime = DateTime.Now; if ((nowtime - _lastPushDebounceTime).TotalMilliseconds > DebounceDelayMs) { Console.WriteLine($"버튼 눌림 감지: {item.Description}"); item.d_Actor?.Invoke(item.Lineoffset); _lastPushDebounceTime = nowtime; } } else if (currentValue == 1 && item.LastValue == 0) { //버튼 떼짐 감지 DateTime nowtime = DateTime.Now; if ((nowtime - _lastPullDebounceTime).TotalMilliseconds > DebounceDelayMs) { Console.WriteLine($"버튼 떼임 감지: {item.Description}"); _lastPullDebounceTime = nowtime; } } // 상태 업데이트 item.LastValue = currentValue; } //한바퀴 다돌고 5ms Sleep Thread.Sleep(5); } } /// <summary> /// 자원 해지 /// </summary> public void Dispose() { foreach (var config in _buttonConfigs) { NativeBindings.GpiodLineRelease(config.LinePtr); } if (_chipPtr != IntPtr.Zero) { NativeBindings.GpiodChipClose(_chipPtr); Console.WriteLine("GPIO 자원 해제 완료."); } }}using System;using System.Collections.Generic;namespace GpioTest;/// <summary>/// 메인 클래스/// </summary>public class MainApp{ /// <summary> /// 주진입점 /// doent빌드 명령어: dotnet publish -c Release -r linux-arm64 /// </summary> public static void Main(string[] args) { //모니터링할 버튼 리스트 정의 //(PC6=70) var buttonConfigurations = new List<GpioButtonConfig> { new GpioButtonConfig { Lineoffset = 70, Description = "Button PC6" }, }; // GpioMonitor 인스턴스를 using 구문으로 생성하여 자동 해제되도록 처리 using var monitor = new GpioMonitor(); bool bsetup = monitor.Initialize(buttonConfigurations); if (!bsetup) { System.Console.WriteLine("GPIO버튼 초기화 실패!!"); Environment.Exit(0); } //콘솔 종료키 감지 Console.CancelKeyPress += (sender, e) => { System.Console.WriteLine(Environment.NewLine + "종료요청 감지.."); e.Cancel = true; Environment.Exit(0); }; //모니터링 시작 try { monitor.StartMonitoring(); } catch (Exception ex) { Console.WriteLine($"런타임 오류: {ex.Message}"); } }}