diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 05bb0cd..6410f60 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -38,3 +38,9 @@ jobs: - name: Build run: msbuild.exe NINASonyCameraPlugin.sln /nologo /nr:false /p:DeployOnBuild=true /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:DeleteExistingFiles=True /p:platform="Any CPU" /p:configuration="Release" /p:PublishUrl="../_build" + - name: Upload SonyCamera DLL + uses: actions/upload-artifact@v4 + with: + name: SonyCamera-DLL + path: | + **/bin/Release/**/NINA.RetroKiwi.Plugin.SonyCamera.dll \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e894b42..870006f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Sony Camera Plugin +## 1.0.0.4 +* Added the `UpdateSubSampleArea` implementation and bumped the minimum NINA version so the plugin loads in 3.2. +* Restored the ISO/Gain UI by notifying NINA whenever the camera connection updates ISO data and by handling cameras without ISO option lists. +* Probed the registry ISO property (`0xFFFE`) so older Sony bodies still populate the gain dropdown. +* Logged clearer errors when gain min/max cannot be determined and improved the GitHub workflow to publish the release DLL artifact. + ## 1.0.0.3 Updated to support new device property "DisplayName" required in NINA 3 diff --git a/Drivers/CameraDriver.cs b/Drivers/CameraDriver.cs index c928898..f2d6826 100644 --- a/Drivers/CameraDriver.cs +++ b/Drivers/CameraDriver.cs @@ -25,9 +25,9 @@ namespace NINA.RetroKiwi.Plugin.SonyCamera.Drivers { public class CameraDriver : BaseINPC, ICamera { // Some camera settings we are interested in - private const uint PROPID_BATTERY = 53784; - private const uint PROPID_ISO = 53790; // Actual ISO currently set - private const uint PROPID_ISOS = 65534; // List of learnt ISOs this camera supports + private const uint PROPID_BATTERY = 53784; + private const uint PROPID_ISO = 0xD21E; // Actual ISO currently set + private const uint PROPID_ISOS = 0xFFFE; // Registry-backed list of learnt ISOs (may be empty until learnt) // Capture Status private const uint CAPTURE_CREATED = 0x0000; @@ -60,6 +60,37 @@ private PropertyValue GetPropertyValue(uint id) { return SonyDriver.GetInstance().GetProperty(_camera.Handle, id); } + private IReadOnlyList GetAvailableIsoOptions() { + if (_camera == null) { + return Array.Empty(); + } + + uint[] propertyCandidates = { PROPID_ISOS, PROPID_ISO }; + + foreach (var propertyId in propertyCandidates) { + try { + var options = _camera.GetPropertyInfo(propertyId)?.Options()?.Where(o => o.Value <= 0x00FFFFFF).ToList(); + if (options != null && options.Count > 0) { + return options; + } + } catch (Exception ex) { + Logger.Warning($"Unable to enumerate ISO options for property 0x{propertyId:X}: {ex.Message}"); + } + } + + Logger.Warning("Camera did not report any ISO options via known properties (registry ISO list may be empty until the camera learns it)."); + return Array.Empty(); + } + + private void NotifyGainPropertiesChanged() { + RaisePropertyChanged(nameof(CanGetGain)); + RaisePropertyChanged(nameof(CanSetGain)); + RaisePropertyChanged(nameof(GainMin)); + RaisePropertyChanged(nameof(GainMax)); + RaisePropertyChanged(nameof(Gain)); + RaisePropertyChanged(nameof(Gains)); + } + #endregion #region Supported Properties @@ -211,45 +242,35 @@ public int BitDepth { } } - public bool CanGetGain { - get { - if (_camera != null) { - PropertyInfo gainInfo = _camera.GetPropertyInfo(PROPID_ISOS); - - if (gainInfo != null && gainInfo.Options().Any()) { - return true; - } else { - return false; - } - } else { - return false; - } - } - } + public bool CanGetGain => GetAvailableIsoOptions().Any(); public bool CanSetGain => CanGetGain; public int GainMax { get { - if (_camera != null) { - PropertyInfo gainInfo = _camera.GetPropertyInfo(PROPID_ISOS); - - return (int)gainInfo.Options().Last().Value; - } else { - return 0; + var isoOptions = GetAvailableIsoOptions(); + if (!isoOptions.Any()) { + if (_camera != null) { + Logger.Error("Problem getting gain max: camera did not report ISO options."); + } + return -1; } + + return (int)isoOptions.Last().Value; } } public int GainMin { get { - if (_camera != null) { - PropertyInfo gainInfo = _camera.GetPropertyInfo(PROPID_ISOS); - - return (int)gainInfo.Options().Min(o => o.Value); - } else { + var isoOptions = GetAvailableIsoOptions(); + if (!isoOptions.Any()) { + if (_camera != null) { + Logger.Error("Problem getting gain min: camera did not report ISO options."); + } return -1; } + + return (int)isoOptions.Min(o => o.Value); } } @@ -258,7 +279,6 @@ public int Gain { if (_camera != null) { try { PropertyValue value = GetPropertyValue(PROPID_ISO); - PropertyInfo gainInfo = _camera.GetPropertyInfo(PROPID_ISOS); return (int)(value.Value == 0xffffff ? 0 : value.Value); } catch (Exception ex) { @@ -274,6 +294,7 @@ public int Gain { if (_camera != null) { try { SonyDriver.GetInstance().SetProperty(_camera.Handle, PROPID_ISO, (uint)value); + RaisePropertyChanged(nameof(Gain)); } catch (Exception ex) { Logger.Error($"Problem setting gain to {value}", ex); } @@ -285,17 +306,11 @@ public IList Gains { get { List gains = new List(); - if (_camera != null) { - PropertyInfo gainInfo = _camera.GetPropertyInfo(PROPID_ISOS); - - foreach (var iso in gainInfo.Options()) { - if (iso.Value == 0xffffff) { - // AUTO - gains.Add(0); - } - else { - gains.Add((int)iso.Value); - } + foreach (var iso in GetAvailableIsoOptions()) { + if (iso.Value == 0xffffff) { + gains.Add(0); // AUTO + } else { + gains.Add((int)iso.Value); } } @@ -343,6 +358,7 @@ public double TemperatureSetPoint { set { } } + public bool CanSubSample => false; public bool EnableSubSample { get; set; } @@ -441,12 +457,12 @@ public Task Connect(CancellationToken token) { return Task.Run(() => { try { _camera = SonyDriver.GetInstance().OpenCamera(_device.Id); - } - catch (Exception ex) { + } catch (Exception ex) { Logger.Error(ex); _camera = null; } + NotifyGainPropertiesChanged(); return _camera != null; }); } @@ -460,6 +476,7 @@ public void Disconnect() { } _camera = null; + NotifyGainPropertiesChanged(); } } @@ -468,7 +485,8 @@ public Task DownloadLiveView(CancellationToken token) { using (var memStream = new MemoryStream(SonyDriver.GetInstance().GetLiveView(_camera.Handle))) { memStream.Position = 0; - JpegBitmapDecoder decoder = new JpegBitmapDecoder(memStream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad); + JpegBitmapDecoder decoder = + new JpegBitmapDecoder(memStream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.OnLoad); FormatConvertedBitmap bitmap = new FormatConvertedBitmap(); bitmap.BeginInit(); @@ -482,15 +500,16 @@ public Task DownloadLiveView(CancellationToken token) { var metaData = new ImageMetaData(); return _exposureDataFactory.CreateImageArrayExposureData( - input: outArray, - width: bitmap.PixelWidth, - height: bitmap.PixelHeight, - bitDepth: 16, - isBayered: false, - metaData: metaData); + input: outArray, + width: bitmap.PixelWidth, + height: bitmap.PixelHeight, + bitDepth: 16, + isBayered: false, + metaData: metaData); } }); } + public void SetupDialog() { throw new NotImplementedException(); } @@ -500,7 +519,8 @@ public void StartExposure(CaptureSequence sequence) { SonyDriver driver = SonyDriver.GetInstance(); uint captureStatus = driver.GetCaptureStatus(_camera.Handle); - if (captureStatus == CAPTURE_CAPTURING || captureStatus == CAPTURE_PROCESSING || captureStatus == CAPTURE_STARTING || captureStatus == CAPTURE_READING || captureStatus == CAPTURE_PROCESSING) { + if (captureStatus == CAPTURE_CAPTURING || captureStatus == CAPTURE_PROCESSING || captureStatus == CAPTURE_STARTING || + captureStatus == CAPTURE_READING || captureStatus == CAPTURE_PROCESSING) { Notification.ShowWarning("Another exposure still in progress. Cancelling it to start another."); } @@ -508,7 +528,7 @@ public void StartExposure(CaptureSequence sequence) { driver.CancelCapture(_camera.Handle); double exposureTime = sequence.ExposureTime; - driver.StartCapture(_camera.Handle, (float)exposureTime);//); + driver.StartCapture(_camera.Handle, (float)exposureTime); //); } } @@ -530,7 +550,8 @@ public async Task WaitUntilExposureIsReady(CancellationToken token) { try { uint captureStatus = driver.GetCaptureStatus(_camera.Handle); - Logger.Info($"Waiting for image to be ready, current state is {captureStatus}, completion states are {String.Join(", ", completionStates)}"); + Logger.Info( + $"Waiting for image to be ready, current state is {captureStatus}, completion states are {String.Join(", ", completionStates)}"); while (!completionStates.Contains(captureStatus)) { await CoreUtil.Wait(TimeSpan.FromMilliseconds(100), token); @@ -583,6 +604,28 @@ public string SendCommandString(string command, bool raw = true) { public void SetBinning(short x, short y) { // Ignore } + + public void UpdateSubSampleArea() { + if (_camera == null) { + EnableSubSample = false; + SubSampleX = 0; + SubSampleY = 0; + SubSampleWidth = 0; + SubSampleHeight = 0; + return; + } + + if (EnableSubSample && !CanSubSample) { + Logger.Warning("Sub-sampling requested but not supported for Sony cameras. Falling back to full frame."); + EnableSubSample = false; + } + + // Sony cameras currently expose the entire frame, so always reset to the sensor dimensions. + SubSampleX = 0; + SubSampleY = 0; + SubSampleWidth = _camera.ImageSize.Width; + SubSampleHeight = _camera.ImageSize.Height; + } #endregion diff --git a/NINASonyCameraPlugin.csproj b/NINASonyCameraPlugin.csproj index d0b7dd3..949eaca 100644 --- a/NINASonyCameraPlugin.csproj +++ b/NINASonyCameraPlugin.csproj @@ -12,7 +12,7 @@ - + @@ -21,7 +21,6 @@ - @@ -29,43 +28,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -99,6 +69,6 @@ - + - \ No newline at end of file + diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 81e05d9..ad54320 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ // [MANDATORY] The assembly versioning //Should be incremented for each new release build of a plugin -[assembly: AssemblyVersion("1.0.0.3")] -[assembly: AssemblyFileVersion("1.0.0.3")] +[assembly: AssemblyVersion("1.0.0.4")] +[assembly: AssemblyFileVersion("1.0.0.4")] // [MANDATORY] The name of your plugin [assembly: AssemblyTitle("Sony Camera Plugin")] @@ -25,7 +25,7 @@ [assembly: AssemblyCopyright("Copyright © 2023-2024 Doug Henderson")] // The minimum Version of N.I.N.A. that this plugin is compatible with -[assembly: AssemblyMetadata("MinimumApplicationVersion", "3.0.0.2001")] +[assembly: AssemblyMetadata("MinimumApplicationVersion", "3.2.0.3001")] // The license your plugin code is using [assembly: AssemblyMetadata("License", "MPL-2.0")] @@ -72,6 +72,9 @@ * [My camera is not supported](https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Installation#if-you-dont-have-a-supported-camera-model) * [List of supported cameras](https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Supported-Cameras) * [Other known issues](https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Troubleshooting) + +## Contributors +* Lucas Lepski [@ShurkanTwo](https://github.com/shurkanTwo) ")] // Setting ComVisible to false makes the types in this assembly not visible diff --git a/README.md b/README.md index 56f348a..a98c5bd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,52 @@ -# Sony Camera Plugin +# Sony Camera Plugin +Native NINA plugin for Sony mirrorless/DSLR bodies over the Sony MTP interface, so you can control the camera directly from NINA without ASCOM. + +## Features + +- Supports many Sony cameras (supported list: https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Supported-Cameras) +- Connects Sony bodies and surfaces their model name in NINA +- LiveView streaming (16-bit monochrome, where supported) plus normal captures saved as ARW through NINA's RAW converter so full metadata is preserved +- Gain dropdown populated with actual ISO values (including Auto when present); ISO is controllable via the Gain property +- Battery level reporting; exposes camera pixel size and resolution +- Camera-controlled lenses exposed as a focuser inside NINA (supported lenses: https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Supported-Lenses) +- Handles cameras that do not expose ISO options until they are "learned" in-camera + +## Requirements + +- NINA 3.2 or newer (built against `NINA.Plugin` 3.2.x) +- Windows 10 1809 or newer (.NET 8, WPF) +- Sony body set to PC Remote/MTP mode +- This plugin uses the core (non-ASCOM) files from the Sony ASCOM driver v1.0.1.17 or later; install that driver from https://github.com/dougforpres/ASCOMSonyCameraDriver/releases so the shared components are present. + +## Installation + +Install via the N.I.N.A. plugin manager or manually download and copy the DLL file into `%LOCALAPPDATA%\\NINA\\Plugins\\..\\Sony Camera Plugin\\` from: +https://github.com/dougforpres/NINASonyCameraPlugin/releases + +## Usage notes + +- ISO/gain list may be empty until the camera has learned ISO options; set an ISO on-camera once if needed. +- Temperature is not reported (the plugin does not request processed ARW metadata). +- Binning and sub-sampling are not supported; captures use the full sensor frame. +- For exposures <= 30s the driver chooses the nearest built-in shutter speed; longer exposures fall back to Bulb. +- Powering off/unplugging the camera while connected can crash NINA via Windows' MTP stack (PortableDeviceApi.dll access violation); disconnect in NINA first to avoid it. + +## Support + +- Best contact is email via the Homepage link or the ASCOM driver troubleshooting page footer. +- You may be asked for a driver log when reporting bugs: https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Troubleshooting#the-driver-dll-log +- Help links: + - My camera is not supported (https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Installation#if-you-dont-have-a-supported-camera-model); + - Supported cameras (https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Supported-Cameras); + - Other known issues (https://github.com/dougforpres/ASCOMSonyCameraDriver/wiki/Troubleshooting) + +## Metadata + +- Version: 1.0.0.4 +- Author: Doug Henderson +- Contributors: Lucas Lepski [@ShurkanTwo](https://github.com/shurkanTwo) +- Minimum NINA version: 3.2.0.3001 +- License: MPL-2.0 (https://www.mozilla.org/en-US/MPL/2.0/) +- Homepage: https://retro.kiwi +- Changelog: https://github.com/dougforpres/NINASonyCameraPlugin/blob/master/CHANGELOG.md