progress on transfer loop.

This commit is contained in:
Eugene Wang
2021-04-28 07:19:02 -04:00
parent c8e101dd0d
commit 4d17226711
5 changed files with 260 additions and 76 deletions

View File

@@ -11,7 +11,7 @@ namespace Net5Console
class Program class Program
{ {
[STAThread] [STAThread]
static void Main(string[] args) static void Main()
{ {
Console.WriteLine("Starting twain test in console..."); Console.WriteLine("Starting twain test in console...");
Console.WriteLine(); Console.WriteLine();
@@ -21,29 +21,21 @@ namespace Net5Console
{ {
session.DeviceEvent += (sender, e) => session.DeviceEvent += (sender, e) =>
{ {
Console.WriteLine($"Got device event " + (TWDE)e.Event); Console.WriteLine($"Got device event " + e.Event);
Console.WriteLine(); Console.WriteLine();
}; };
session.ScanEvent += (sender, closing) => session.TransferError += (sender, e) =>
{ {
Console.WriteLine($"Got scan event " + closing); Console.WriteLine($"Transfer error {e.Code} {e.Exception}.");
};
session.TransferReady += (sender, e) =>
{
Console.WriteLine($"Transfer ready, count={e.PendingCount}.");
};
session.SourceDisabled += (sender,e)=>
{
Console.WriteLine("Disabled device.");
Console.WriteLine(); Console.WriteLine();
// don't care, just end it
TW_PENDINGXFERS pending = default;
var sts = session.TWAIN.DatPendingxfers(DG.CONTROL, MSG.RESET, ref pending);
TW_USERINTERFACE twuserinterface = default;
if (session.TWAIN.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface) == STS.SUCCESS)
{
Console.WriteLine("Disabled device.");
Console.WriteLine();
}
else
{
Console.Error.WriteLine("Failed to disabled device.");
Console.WriteLine();
}
hold.Set(); hold.Set();
}; };
@@ -135,6 +127,11 @@ namespace Net5Console
} }
private static void Session_SourceDisabled(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private static void WriteCapInfo(Capabilities caps, CAP cap) private static void WriteCapInfo(Capabilities caps, CAP cap)
{ {
// use reflection due to unknown generics // use reflection due to unknown generics

View File

@@ -10,20 +10,8 @@ namespace NTwain
/// <summary> /// <summary>
/// Some utility extension methods. /// Some utility extension methods.
/// </summary> /// </summary>
public static class ExtensionMethods static class ExtensionMethods
{ {
/// <summary>
/// Checks if the returned <see cref="STS"/> contains a particular
/// <see cref="STS"/> value since normal TWAIN
/// return code is merged with condition code into <see cref="STS"/>.
/// This is only useful for non-success values.
/// </summary>
/// <param name="status"></param>
/// <param name="checkFor">Non-success code to check for.</param>
/// <returns></returns>
public static bool Is(this STS status, STS checkFor)
{
return (status & checkFor) == checkFor;
}
} }
} }

View File

@@ -0,0 +1,27 @@
using System;
using TWAINWorkingGroup;
namespace NTwain
{
/// <summary>
/// Contains TWAIN codes and source status when an error is encountered during transfer.
/// </summary>
public class TransferErrorEventArgs : EventArgs
{
public TransferErrorEventArgs(STS code, Exception error = null)
{
Code = code;
Exception = error;
}
/// <summary>
/// Gets the error code from the transfer.
/// </summary>
public STS Code { get; }
/// <summary>
/// Gets the exception if the error is from some exception.
/// </summary>
public Exception Exception { get; }
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TWAINWorkingGroup;
namespace NTwain
{
/// <summary>
/// Contains event data when a data transfer is ready to be processed.
/// </summary>
public class TransferReadyEventArgs : EventArgs
{
public TransferReadyEventArgs(TWAIN twain, int pendingCount, TWEJ endOfJobFlag)
{
_twain = twain;
PendingCount = pendingCount;
EndOfJobFlag = endOfJobFlag;
}
/// <summary>
/// Gets or sets whether the current transfer should be skipped
/// and continue next transfer if there are more data.
/// </summary>
public bool SkipCurrent { get; set; }
/// <summary>
/// Gets or sets whether to cancel the capture phase.
/// </summary>
public CancelType CancelCapture { get; set; }
/// <summary>
/// Gets the end of job flag value for this transfer if job control is enabled.
/// </summary>
public TWEJ EndOfJobFlag { get; private set; }
/// <summary>
/// Gets the known pending transfer count. This may not be appilicable
/// for certain scanning modes.
/// </summary>
public int PendingCount { get; private set; }
TW_IMAGEINFO? _imgInfo;
private readonly TWAIN _twain;
/// <summary>
/// Gets the tentative image information for the current transfer if applicable.
/// This may differ from the final image depending on the transfer mode used (mostly when doing mem xfer).
/// </summary>
public TW_IMAGEINFO? PendingImageInfo
{
get
{
if (!_imgInfo.HasValue)
{
TW_IMAGEINFO info = default;
if (_twain.DatImageinfo(DG.IMAGE, MSG.GET, ref info) == STS.SUCCESS)
{
_imgInfo = info;
}
}
return _imgInfo;
}
}
}
public enum CancelType
{
/// <summary>
/// No cancel.
/// </summary>
None,
/// <summary>
/// Stops feeder but continue receiving scanned images.
/// </summary>
Graceful,
/// <summary>
/// Stops feeder and discard pending images.
/// </summary>
Immediate
}
}

View File

@@ -4,8 +4,10 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using TWAINWorkingGroup; using TWAINWorkingGroup;
@@ -80,8 +82,20 @@ namespace NTwain
/// </summary> /// </summary>
public event EventHandler SourceDisabled; public event EventHandler SourceDisabled;
/// <summary>
/// Raised when there's some error during transfer.
/// </summary>
public event EventHandler<TransferErrorEventArgs> TransferError;
/// <summary>
/// Raised when there's a pending transfer. Can be used to cancel transfers.
/// </summary>
public event EventHandler<TransferReadyEventArgs> TransferReady;
private void HandleUIThreadAction(Action action) private void HandleUIThreadAction(Action action)
{ {
DebugThreadInfo("begin");
_threadMarshaller.Invoke(action); _threadMarshaller.Invoke(action);
} }
@@ -93,6 +107,8 @@ namespace NTwain
// Drain the event queue... // Drain the event queue...
while (true) while (true)
{ {
DebugThreadInfo("in loop");
// Try to get an event... // Try to get an event...
twdeviceevent = default; twdeviceevent = default;
sts = _twain.DatDeviceevent(DG.CONTROL, MSG.GET, ref twdeviceevent); sts = _twain.DatDeviceevent(DG.CONTROL, MSG.GET, ref twdeviceevent);
@@ -116,23 +132,103 @@ namespace NTwain
private STS HandleScanEvent(bool closing) private STS HandleScanEvent(bool closing)
{ {
DebugThreadInfo("begin");
// the scan event needs to return asap since it can come from msg loop // the scan event needs to return asap since it can come from msg loop
// so fire off the handling work to another thread // so fire off the handling work to another thread
_threadMarshaller.BeginInvoke(new Func<bool, STS>(HandleScanEventReal), closing); _threadMarshaller.BeginInvoke(new Action<bool>(HandleScanEventReal), closing);
return STS.SUCCESS; return STS.SUCCESS;
} }
// tracks transfer state
bool _xferReadySent;
bool _disableDsSent;
void HandleScanEventReal(bool closing) void HandleScanEventReal(bool closing)
{ {
//// Scoot... DebugThreadInfo("begin");
//if (_twain == null) return STS.FAILURE;
//// We're superfluous... if (_twain == null || State <= STATE.S4 || closing) return;
//if (_twain.GetState() <= STATE.S4 || closing) return STS.SUCCESS;
if (_twain.IsMsgCloseDsReq() || _twain.IsMsgCloseDsOk())
{
StepDown(STATE.S4);
return;
}
// all except mem xfer will run this once and raise event.
// mem xfer will run this multiple times until complete image is assembled
if (_twain.IsMsgXferReady())
{
TW_PENDINGXFERS pending = default;
var sts = _twain.DatPendingxfers(DG.CONTROL, MSG.GET, ref pending);
if (sts != STS.SUCCESS)
{
try
{
TransferError?.Invoke(this, new TransferErrorEventArgs(sts));
}
catch { }
return; // do more?
}
var xferMech = Capabilities.ICAP_XFERMECH.GetCurrent();
var readyArgs = new TransferReadyEventArgs(_twain, pending.Count, (TWEJ)pending.EOJ);
try
{
TransferReady?.Invoke(this, readyArgs);
}
catch { }
if (readyArgs.CancelCapture == CancelType.Immediate)
{
sts = _twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref pending);
}
else
{
if (readyArgs.CancelCapture == CancelType.Graceful) StopCapture();
if (!readyArgs.SkipCurrent)
{
switch (xferMech)
{
case TWSX.NATIVE:
RunImageNativeXfer();
break;
case TWSX.MEMFILE:
RunImageMemFileXfer();
break;
case TWSX.FILE:
RunImageFileXfer();
break;
case TWSX.MEMORY:
RunImageMemoryXfer();
break;
}
}
sts = _twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref pending);
}
// TODO: may be wrong for now
if (pending.Count == 0 || sts == STS.CANCEL || sts == STS.XFERDONE)
{
StepDown(STATE.S4);
}
else
{
HandleScanEvent(State <= STATE.S3);
}
}
}
[Conditional("DEBUG")]
private void DebugThreadInfo(string description, [CallerMemberName] string callerName = "")
{
var tid = Thread.CurrentThread.ManagedThreadId;
Debug.WriteLine($"[Thread {tid}] {callerName}() {description}");
}
private void RunImageMemoryXfer()
{
throw new NotImplementedException();
//// Handle DAT_NULL/MSG_XFERREADY... //// Handle DAT_NULL/MSG_XFERREADY...
//if (_twain.IsMsgXferReady() && !_xferReadySent) //if (_twain.IsMsgXferReady() && !_xferReadySent)
@@ -161,20 +257,6 @@ namespace NTwain
// } // }
//} //}
//// Handle DAT_NULL/MSG_CLOSEDSREQ...
//if (_twain.IsMsgCloseDsReq() && !_disableDsSent)
//{
// _disableDsSent = true;
// StepDown(STATE.S4);
//}
//// Handle DAT_NULL/MSG_CLOSEDSOK...
//if (_twain.IsMsgCloseDsOk() && !_disableDsSent)
//{
// _disableDsSent = true;
// StepDown(STATE.S4);
//}
//// This is where the statemachine runs that transfers and optionally //// This is where the statemachine runs that transfers and optionally
//// saves the images to disk (it also displays them). It'll go back //// saves the images to disk (it also displays them). It'll go back
//// and forth between states 6 and 7 until an error occurs, or until //// and forth between states 6 and 7 until an error occurs, or until
@@ -183,13 +265,21 @@ namespace NTwain
//{ //{
// CaptureImages(); // CaptureImages();
//} //}
}
private void RunImageFileXfer()
{
throw new NotImplementedException();
}
private void RunImageMemFileXfer()
{
throw new NotImplementedException();
}
private void RunImageNativeXfer()
{
//// Trigger the next event, this is where things all chain together.
//// We need begininvoke to prevent blockking, so that we don't get
//// backed up into a messy kind of recursion. We need DoEvents,
//// because if things really start moving fast it's really hard for
//// application events, like button clicks to break through...
//HandleScanEvent(_twain.GetState() <= STATE.S3);
} }
//protected virtual void OnScanEvent(bool closing) { } //protected virtual void OnScanEvent(bool closing) { }
@@ -301,34 +391,31 @@ namespace NTwain
/// <summary> /// <summary>
/// Steps down the TWAIN state to the specified state. /// Steps down the TWAIN state to the specified state.
/// </summary> /// </summary>
/// <param name="state"></param> /// <param name="target"></param>
public void StepDown(STATE state) public void StepDown(STATE target)
{ {
TW_PENDINGXFERS twpendingxfers = default;
// Make sure we have something to work with... // Make sure we have something to work with...
if (_twain == null) if (_twain == null) return;
{
return;
}
// Walk the states, we don't care about the status returns. Basically, // Walk the states, we don't care about the status returns. Basically,
// these need to work, or we're guaranteed to hang... // these need to work, or we're guaranteed to hang...
// 7 --> 6 // 7 --> 6
if ((_twain.GetState() == STATE.S7) && (state < STATE.S7)) if ((State == STATE.S7) && (target < STATE.S7))
{ {
TW_PENDINGXFERS twpendingxfers = default;
_twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref twpendingxfers); _twain.DatPendingxfers(DG.CONTROL, MSG.ENDXFER, ref twpendingxfers);
} }
// 6 --> 5 // 6 --> 5
if ((_twain.GetState() == STATE.S6) && (state < STATE.S6)) if ((State == STATE.S6) && (target < STATE.S6))
{ {
TW_PENDINGXFERS twpendingxfers = default;
_twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref twpendingxfers); _twain.DatPendingxfers(DG.CONTROL, MSG.RESET, ref twpendingxfers);
} }
// 5 --> 4 // 5 --> 4
if ((_twain.GetState() == STATE.S5) && (state < STATE.S5)) if ((State == STATE.S5) && (target < STATE.S5))
{ {
TW_USERINTERFACE twuserinterface = default; TW_USERINTERFACE twuserinterface = default;
_twain.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface); _twain.DatUserinterface(DG.CONTROL, MSG.DISABLEDS, ref twuserinterface);
@@ -336,14 +423,14 @@ namespace NTwain
} }
// 4 --> 3 // 4 --> 3
if ((_twain.GetState() == STATE.S4) && (state < STATE.S4)) if ((State == STATE.S4) && (target < STATE.S4))
{ {
_caps = null; _caps = null;
_twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref _twain.m_twidentityDs); _twain.DatIdentity(DG.CONTROL, MSG.CLOSEDS, ref _twain.m_twidentityDs);
} }
// 3 --> 2 // 3 --> 2
if ((_twain.GetState() == STATE.S3) && (state < STATE.S3)) if ((State == STATE.S3) && (target < STATE.S3))
{ {
_twain.DatParent(DG.CONTROL, MSG.CLOSEDSM, ref _hWnd); _twain.DatParent(DG.CONTROL, MSG.CLOSEDSM, ref _hWnd);
} }