Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ ErpNet.FP.Server/appsettings.json
ErpNet.FP.Setup/ProductInstallFiles.wxs
/UpgradeLog.htm
/ErpNet.FP.Setup/HeatWaveConvert.log

# FashionPoint build artifacts
/publish-win-x64/
/ErpNet.FP-fashionpoint-*
23 changes: 16 additions & 7 deletions ErpNet.FP.Core/Transports/ComTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,42 @@ public class ComTransport : Transport
public override string TransportName => "com";

protected const int DefaultBaudRate = 115200;
protected const int DefaultTimeout = 800;
// FashionPoint patch: 800ms is too aggressive for slow fiscal devices
// (e.g. Daisy PerfectS pauses >800ms between SYN bytes while writing
// to fiscal memory on receipt close), causing E107 "no response",
// automatic receipt aborts (АНУЛИРАН БОН) and false failures.
protected const int DefaultTimeout = 3000;
protected const int DefaultTimeoutToClose = 3000; // dispose SerialPort object when specified time after communication is elapsed

private readonly IDictionary<string, ComTransport.Channel?> openedChannels =
new Dictionary<string, ComTransport.Channel?>();

public override IChannel OpenChannel(string address)
{
var (comPort, baudRate) = ParseAddress(address);
var (comPort, baudRate, timeout) = ParseAddress(address);
if (openedChannels.TryGetValue(comPort, out Channel? channel))
{
if (channel != null)
return channel;
else
openedChannels.Remove(comPort);
}
channel = new Channel(comPort, baudRate);
channel = new Channel(comPort, baudRate, timeout);
// <address> is more specific (<Com>:<Speed>) than <comPort> and multiple addresses on one port can lead to locking or not finding device (only one can be opened)
openedChannels.Add(comPort, channel);
return channel;
}

protected (string, int) ParseAddress(string address)
// FashionPoint patch: address format is <Com>[:<Speed>[:<TimeoutMs>]],
// e.g. "COM5", "COM5:115200", "COM5:115200:5000".
protected (string, int, int) ParseAddress(string address)
{
var parts = address.Split(':');
if (parts.Length == 1) return (address, DefaultBaudRate);
if (parts.Length == 1) return (address, DefaultBaudRate, DefaultTimeout);
var hostName = parts[0];
var baudRate = parts.Length > 1 ? int.Parse(parts[1]) : DefaultBaudRate;
return (hostName, baudRate);
var timeout = parts.Length > 2 ? int.Parse(parts[2]) : DefaultTimeout;
return (hostName, baudRate, timeout);
}

public override void Drop(IChannel channel)
Expand All @@ -69,6 +76,7 @@ public class Channel : IChannel
internal /*readonly*/ SerialPort? serialPort; // can be disposed and created multiple times
private string portName;
private int baudRate;
private int timeout;
protected Timer idleTimer;
protected const int MinimalBaudRate = 9600;

Expand All @@ -88,7 +96,7 @@ public string Descriptor
}
}

public SerialPort GetNewSerialPort(int timeout = DefaultTimeout)
public SerialPort GetNewSerialPort()
{
idleTimer.Change(DefaultTimeoutToClose, 0);
return new SerialPort
Expand All @@ -107,6 +115,7 @@ public Channel(string portName, int baudRate = DefaultBaudRate, int timeout = De
{
this.portName = portName;
this.baudRate = baudRate;
this.timeout = timeout;

serialPort = null; // GetNewSerialPort();
idleTimer = new Timer(IdleTimerElapsed);
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
<img src="https://github.com/erpnet/ErpNet.FP/raw/master/ErpNet.FP.Server/ErpNet.FP.thumb.png" align="right"/>

> ## 🔱 FashionPoint Fork
>
> Това е форк на [erpnet/ErpNet.FP](https://github.com/erpnet/ErpNet.FP), поддържан от **FashionPoint** за нашите клиенти (салони/клиники) с фискални устройства.
>
> ### Защо съществува
> Бавни касови апарати (напр. **Daisy PerfectS**) правят пауза от над 800ms между SYN байтовете, докато записват бона във фискалната памет при затваряне. Оригиналният ErpNet.FP има **хардкоднат сериен timeout от 800ms**, което води до:
> - грешка **E107 "no response"**, въпреки че бонът реално се записва;
> - автоматично анулиране на бона от драйвера → **„АНУЛИРАН БОН"** на хартията;
> - фалшиви „провалени" продажби и дублирани УНП в POS системата (проблем по Наредба Н-18).
>
> ### Какво е променено спрямо оригинала
> - **Серийният timeout е вдигнат 800ms → 3000ms** (`ComTransport.DefaultTimeout`);
> - **Per-принтер timeout през адреса**: `com://COM5:115200:5000` (порт : скорост : timeout в ms) — бъдещи бавни устройства се решават с настройка, без ребилд. Напълно обратно съвместимо.
>
> Единственият променен файл е [`ErpNet.FP.Core/Transports/ComTransport.cs`](ErpNet.FP.Core/Transports/ComTransport.cs).
>
> ### Сваляне (нашите билдове)
> Готови билдове за **Windows x64**, **macOS Intel** и **macOS Apple Silicon** — в [**Releases**](https://github.com/fashionpoint-bg/ErpNet.FP/releases). Инструкции за инсталация има във всеки релийз.
>
> ### Статус спрямо оригинала
> Фиксът е предложен upstream: [erpnet/ErpNet.FP#205](https://github.com/erpnet/ErpNet.FP/pull/205). Ако бъде приет, форкът остава само като билд/дистрибуционна точка за нашите клиенти.
>
> Работен бранч: `fashionpoint` (дефолтен). Ъпдейти от оригинала: `git fetch upstream && git merge upstream/master`.

---

# ErpNet.FP

ErpNet.FP is a light-weight multi-platform Http server facilitating printing to fiscal printers through simple JSON Api. The library provides methods to detect, get status, print receipts, reports and other documents to fiscal printers.
Expand Down