srecord noeq FollowTickerFile(File tickerFile) is AutoCloseable { settable int historyEntriesToGrab; settable int interval = 250; // internal, may change TailFile tailFile; event historicLine(S line); event liveLine(S line); gettable volatile long liveTimestamp; gettable volatile S lastLiveLine; settable int maxLineLengthForTickerEntry = 200; bool started() { ret tailFile != null; } bool isLive() { ret liveTimestamp != 0; } void start { if (started()) ret; onLiveLine(line -> { lastLiveLine = line; liveTimestamp = now(); }); long fileSize = l(tickerFile); long start = max(0, fileSize-maxLineLengthForTickerEntry*historyEntriesToGrab); if (historyEntriesToGrab > 0) { S text = loadTextFilePart(tickerFile, start, fileSize); LS lastTickerLines = takeLast(historyEntriesToGrab, lines(text)); for (line : lastTickerLines) historicLine(line); } makeTailFile(fileSize); } void makeTailFile { tailFile = tailFileLinewiseFromPosition(tickerFile, interval, fileSize, l1 liveLine); } close { dispose tailFile; } selfType onLine(IVF1 l) { onHistoricLine(l); ret onLiveLine(l); } toString { ret commaCombine( "FollowTickerFile", stringIf(started(), "started"), !isLive() ? null : "Last live line at " + formatLocalDateWithSeconds(liveTimestamp), tickerFile ); } }