Source file: /~heha/ewa/Reluktanzmotor/maweig-Motor-200831.zip/terminal.cpp

/* Projekt: maweig-Motor
 Teil: Terminal-Steuerung und -Darstellung
	Highlight: Mit Farben und Braille-Grafik
191004	erstellt

*/

#include "Settings.h"
#include "regdef2.h"
#include <cstring>	// memset
#include <limits>
#include "DLOG_4CH.h"	// class Datalog

/************************
 ** Diagrammerstellung **
 ************************/
template<class T=int> union Point{
  struct{
    T x,y;
  };
  T A[2];
  T&operator[](int i) {return A[i];}
  const T&operator[](int i) const{return A[i];}
};

template<class T=int> union Rect{
  struct{
    T l,t,r,b;
  };
  struct{
    Point<T> lt,rb;
  };
  T A[4];
  T w() const {return r-l;}
  T h() const {return b-t;}
  T&operator[](int i) {return A[i];}
  const T&operator[](int i) const{return A[i];}
};

// T kann leider kein Tupel sein
template<class T> void setMin(T&min,T v) {if (min>v) min=v;}
template<class T> void setMax(T&max,T v) {if (max<v) max=v;}

template<class T> class Range{
public:
  T min,max;
  Range() {reset();}
  Range(T startfrom) {reset(startfrom);}
  Range(Range<T>&src) {reset(src);}
  void reset() {reset(std::numeric_limits<T>::max(),std::numeric_limits<T>::min());}
  void reset(const T startfrom) {max=min=startfrom;}
  void reset(const T _min, const T _max) {min=_min, max=_max;}
  void reset(const Range<T>&src) {memcpy(this,&src,sizeof*this);}
  void expand(const T v) {expand(v,v);}
  void expand(const T _min, const T _max) {setMin(min,_min); setMax(max,_max);}
  void expand(const Range<T>&src) {expand(src.min,src.max);}
  void makeSymmetric(T center=0) {setMin(min,center-max+center); setMax(max,center-min+center);}
  const T getDelta() const {return max-min;}
};

template<class TX,class TY> struct Scale{
  virtual TY scale(TX v) const =0;
  virtual TX unscale(TY v) const =0;
  TY operator()(TX v)const{return scale(v);}	// Kurzform von scale()
  TX operator[](TY v)const{return unscale(v);}	// Kurzform von unscale()
};
template<class TX,class TY> struct NoScale:public Scale<TX,TY>{
  virtual TY scale(TX v) const {return TY(v)};
  virtual TX unscale(TY v) const {return TX(v)};
};
template<class TX,class TY,class TF=float> struct LinScale:public Scale<TX,TY>{
  TF factor;
  TY offset;
  LinScale(TF f=1,TY o=0):factor(f),offset(o) {}
  virtual TY scale(TX v) const {return TY(v*factor)+offset;}
  virtual TX unscale(TY v) const {return TX((v-offset)/factor);}
  void TwoPointSetup(TX x1, TY y1, TX x2, TY y2) {
    TF N=TF(x2)-TF(x1);		// hier: Integer-Überläufe vermeiden
    if (N) factor = (TF(y2)-TF(y1))/N;	// ggf. unverändert oder 1 belassen
    offset = y1-TY(factor*x1);
  }
};

static const wchar_t braille[8]={0x2801,0x2808,0x2802,0x2810,0x2804,0x2820,0x2840,0x2880};
//static const wchar_t braille[]=L"⠁⠈⠂⠐⠄⠠⡀⢀";	// geht nicht!

template<unsigned W,unsigned H> class ScreenBuffer {
  wchar_t c[W*H];	// Zeichen
  unsigned a[W*H];	// Attribute (Farben)
public:
  static const unsigned _W=W,_H=H;
  void setpixel(int x,int y,unsigned attr) {
    if ((unsigned)x>=W*2) return;
    if ((unsigned)y>=H*4) return;
    int ofs=(x>>1)+(y>>2)*W;	// Zeichen- und Attribut-Offset
    x = (x&1 | y<<1)&7;		//  0 1		0 3
				//  2 3		1 4
				//  4 5		2 5
				//  6 7		6 7
    a[ofs]=attr;	// Attribut gnadenlos überschreiben (erstmal)
    wchar_t chr=c[ofs];
    if ((chr&0xFF00) != 0x2800) chr=0;	// Braille-Zeichen festlegen
    chr|=braille[x];
    c[ofs]=chr;
  }
  void outchar(int x, int y, wchar_t chr, unsigned attr) {
    if ((unsigned)x>=W*2) return;
    if ((unsigned)y>=H*4) return;
    int ofs=(x>>1)+(y>>2)*W;	// Zeichen- und Attribut-Offset
    c[ofs]=chr;
    a[ofs]=attr;
  }
  int outstring(int x, int y, const wchar_t*str, int len, unsigned attr) {
    if ((unsigned)x>=W*2) return;
    if ((unsigned)y>=H*4) return;
    int ofs=(x>>1)+(y>>2)*W;	// Zeichen- und Attribut-Offset
    if (len<0) len=wcslen(str);
    setMin(len,W-(x>>1));	// Nur bis zum rechten Rand, nicht in Folgezeile
    memcpy(c+ofs,str,len);	// hier: kopiert wchar_t
    memset(a+ofs,attr,len);	// hier: füllt unsigned
    return len;
  }
  void clear() {memset(c,' ',W*H);memset(a,7,W*H);}
  void outscreen() {
    unsigned ca=7;
    for (int y=0; y<H; y++) {
      for (int x=0; x<W; x++) {
	wchar_t cc=c[x+y*W];
	if (cc!=' ') {
	  unsigned aa=a[x+y*W];
	  if (ca!=aa) {
	    ca=aa;
	    unsigned vfarbe=(ca&7)+(ca&8?90:30);
	    unsigned hfarbe=(ca>>4&7)+(ca&128?100:40);
	    com0.printf(L"\e[%u;%um",vfarbe,hfarbe);
	  }
	}
// TODO: Sonstige Attribute (Unterstreichen u.ä.); sprintf mit Attributen
	com0.send(cc);
      }
      com0.send(L"\r\n");
    }
  }
};

const unsigned GRAPH_H=13;

struct DC{
  ScreenBuffer<80,GRAPH_H>&bmp;
  bool validpos;
  Point<> pos;
  unsigned attr;
  DC(ScreenBuffer<80,GRAPH_H>&surface):bmp(surface),validpos(false) {}
  void MoveTo(int x, int y) {pos.x=x; pos.y=y;validpos=true;}
  void LineTo(int x, int y) {
    if (validpos) Line(pos.x,pos.y,x,y);
    MoveTo(x,y);
  }
  void Line(int x0,int y0,int x1,int y1) {	// aus Wikipedia
    if (labs(long(x1)-x0)>=16384) return;
    if (labs(long(y1)-y0)>=16384) return;
    int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
    int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
    int err = dx+dy; /* error value e_xy */
    while(x0!=x1 || y0!=y1) {	// zeichnet letztes Pixel nicht mit
      bmp.setpixel(x0,y0,attr);
      int e2 = 2*err;
      if (e2 > dy) { err += dy; x0 += sx; }
      if (e2 < dx) { err += dx; y0 += sy; }
    }
  }
};

// Dort ist der Datenpuffer
extern DataLog<int,4,160,1> logger;
static const int channel_of_interest = 1;

static void outDiagram() {
// Und hier die Bitmap
  ScreenBuffer<80,GRAPH_H>*bmp=new ScreenBuffer<80,GRAPH_H>;
  bmp->clear();
  for (int x=0; x<160; x+=2) bmp->outchar(x,GRAPH_H*2,L'─',8);
    // Rahmensymbol für Nulllinie in dunkelgrau
  Range<int>minmax(0);
//  int fill=rrdb.getminmax(minmax);
  int fill=logger.rewind();
  for (int i=0; i<fill; i++) minmax.expand(logger.get()[channel_of_interest]);
  logger.rewind();
  minmax.makeSymmetric();
  LinScale<int,int> yscale;
  yscale.TwoPointSetup(minmax.min,GRAPH_H*4-1,minmax.max,0);
  DC dc(*bmp);
  dc.attr=11;	// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR
  for (int i=0; i<fill; i++) {
    dc.LineTo(i,yscale(logger.get()[channel_of_interest]));
  }
  logger.clear();	// nächste Aufnahme mit 10 kSa/s vorbereiten
  bmp->outscreen();
  delete bmp;
}

/************************
 ** Text/Zahlenausgabe **
 ************************/

static void outAdcValues(volatile ADC_RESULT_REGSA&regs) {
  com0.printf(L"ADC%u\t",int(&regs-AdcResultRegsA));
  for (int i=0; i<6; i++) {
    com0.printf(L"%6j",regs.RESULT[i]);
  }
  com0.send(L"\r\n");
}
static void outAdcValues() {
  for (int i=0; i<3; i++) outAdcValues(AdcResultRegsA[i]);	// Adca bis Adcc
}
static void outAmcValues() {
  extern unsigned Amc1210Okay[4];
  extern unsigned Amc1210IR,Amc1210SR;
  extern int Amc1210Data[4];	// in MotorControl.cpp
  DINT;
  unsigned ir=Amc1210IR; Amc1210IR=0;	// bei gesperrten Interrupts lokale Kopien erstellen
  unsigned sr=Amc1210SR; Amc1210SR=0;
  int dat[4];
  memcpy(dat,Amc1210Data,sizeof dat);
  unsigned okay[4];
  memcpy(okay,Amc1210Okay,sizeof okay);
  memset(Amc1210Okay,0,sizeof Amc1210Okay);
  EINT;
  com0.printf(L"AmcSpiA\t");
  for (int i=0; i<4; i++) com0.printf(L"%6j", dat[i]);	// alle 4 Kanäle
// Besser wäre es, hier Effektiv- und Mittelwerte auszuspucken!
  com0.printf(L" %cR = %#04X",'I',ir);
  com0.printf(L" %cR = %#04X",'S',sr);
  com0.send(L"\r\n");
  com0.printf(L"Versuche\t");
  for(int versuch=0; versuch<4; versuch++)
   com0.printf(L" %u.:%u ",versuch+1,okay[versuch]);
  com0.send(L"\r\n");
//  rrdb.put(&dat[1].value);	// Da liegt der Analogwert an
}
static void outSdValues(volatile SDFM_REGSA&regs) {
  com0.printf(L"Sd%u\t",int(&regs-SdfmRegsA));
  for (int i=0; i<4; i++) {
    com0.printf(L"%`12lj",regs[i].I32);
  }
  com0.send(L"\r\n");
}

void Terminal::Update() {
  float T = Adc::myGetTemperatureC(AdcaResultRegs.ADCRESULT0);
  com0.printf(L"\rT = \e[1m%,1f\e[m °C ",T);
  com0.send(L"\e[s" L"\e[?25l" L"\r\n");	// Cursor merken und 1 Zeile runter
  outAdcValues();
  outAmcValues();
  outSdValues(Sdfm1RegsA);
  outSdValues(Sdfm2RegsA);
  outDiagram();
//  com0.printf(L"Gruppierung? %`,5f",12345.67890);
  com0.send(L"\e[?25h" L"\e[u");	// Cursor zurück
// Eingegebene Zeichen (kurz) darstellen
  wchar_t buf[16];
  int l=com0.recv(buf);
  for (int i=0; i<l; i++) {
    wchar_t c=buf[i];
    static const wchar_t tr7[]=L"abtrvfn";
    if (7<=c && c<=13) c=tr7[c-7];
    else if (c==L'\e') c=L'e';
    else if (c==L'\\');
    else if (c<32) {
      com0.printf(L"\\x%02X",c); continue;
    }else{
      com0.send(c); continue;
    }
    com0.send(L'\\');
    com0.send(c);
  }
}
Detected encoding: ASCII (7 bit)8