001package org.vishia.gral.base; 002 003import java.io.BufferedReader; 004import java.io.File; 005import java.io.FileInputStream; 006import java.io.FileReader; 007import java.io.IOException; 008import java.io.InputStream; 009import java.text.DateFormat; 010import java.text.SimpleDateFormat; 011import java.util.ArrayList; 012import java.util.Arrays; 013import java.util.Date; 014import java.util.LinkedList; 015import java.util.List; 016import java.util.concurrent.atomic.AtomicInteger; 017 018import org.vishia.byteData.VariableAccessWithIdx; 019import org.vishia.byteData.VariableAccess_ifc; 020import org.vishia.byteData.VariableContainer_ifc; 021import org.vishia.curves.WriteCurve_ifc; 022import org.vishia.gral.ifc.GralColor; 023import org.vishia.gral.ifc.GralCurveViewTrack_ifc; 024import org.vishia.gral.ifc.GralCurveView_ifc; 025import org.vishia.gral.ifc.GralSetValue_ifc; 026import org.vishia.gral.ifc.GralUserAction; 027import org.vishia.gral.ifc.GralWidget_ifc; 028import org.vishia.mainCmd.Report; 029import org.vishia.mainCmd.ReportWrapperLog; 030import org.vishia.util.Assert; 031import org.vishia.util.Debugutil; 032import org.vishia.util.KeyCode; 033import org.vishia.util.Removeable; 034import org.vishia.util.Timeshort; 035import org.vishia.zbnf.ZbnfJavaOutput; 036import org.vishia.zbnf.ZbnfParser; 037 038 039/**Curve representation for timed values. It is the base class for all implementation. 040 * 041 * <ul> 042 * <li> {@link #setSample(float[], int)}: Write out with new measured values. 043 * </ul> 044 * @see GralCurveViewTrack_ifc. 045 * @author Hartmut Schorrig 046 * 047 */ 048public class GralCurveView extends GralWidget implements GralCurveView_ifc 049{ 050 051 /**Version, history and license. 052 * <ul> 053 * <li>2018-09-20 Hartmut improved for using in InspcCurveViewApp. 054 * <ul> 055 * <li>initTrack(...) renamed to {@link #addTrack(String, String, GralColor, int, int, float, float)} 056 * <li>{@link #searchTrack(String)} if data are read from csv for existing configuration. 057 * <li>{@link #readCurve(File)} now expects names of variable in the first line in any case, not specific format. 058 * <li>{@link #readCurveCsvHeadline(File)} can replace the correct variables in the configuration or builds new tracks 059 * for non existing configurated tracks. 060 * </ul> 061 * <li>2016-07-03 Hartmut chg: {@link GraphicImplAccess} as base class of the implementing class SwtCurveView adequate to the new concept: 062 * An implementing widget is derived from its derived class of {@link GralWidget.ImplAccess}. Therefore only that base class implements the GralWidgetImpl_ifc. 063 * <li>2016-03-06 Hartmut chg: {@link #refreshFromVariable(VariableContainer_ifc)}: Handling of timeshort: If the timeshort starts with a lesser value 064 * it seems to be a new simulation starting with 0. Then the time difference to the last simulation is calculated and added to the timeshort. 065 * It sets a new {@link #setTimePoint(long, int, float)} to prevent overflow later. Initially or if {@link #cleanBuffer()} was invoked the timeshort counts from 0. 066 * Therewith the simulation results have an exact relative time but with the absolute timestamp of the PC's time though the simulation does not supply an absolute time. 067 * Before that change a bug is detected in {@link #prepareIndicesDataForDrawing(int, int, int, boolean)}: If the buffer from right side contains a greater short time, 068 * the indices return only 1 point. It is a bug in this function, not corrected yet because there are not greater times from right to left with the change above. 069 * TODO fix it though. 070 * <li>2016-01-24 Hartmut chg: {@link CommonCurve#lastTimeShort}: Don't store points with the same time stamp. 071 * <li>2016-01-24 Hartmut bugfix: {@link #applySettings(String)} after read cfg: remove previous {@link CommonCurve#timeVariable}, may be a new one! 072 * <li>2015-07-12 Hartmut new: {@link Track#setVisible(int)} to control the visibility of tracks. 073 * <li>2015-07-12 Hartmut new: {@link Track#groupTrackScale(GralCurveViewTrack_ifc)} for groups of tracks with same scaling. 074 * <li>2014-05-20 Hartmut new in {@link #setSample(float[], int)}: write a point only if at least one variable was refreshed. 075 * <li>2014-03-14 Hartmut new: {@link CommonCurve#timeVariable} for time from target. 076 * <li>2014-02-03 Hartmut new: {@link CommonCurve#bFreeze}: freeze as common property of more as one GralCurveView. Constructor argument. 077 * <li>2014-01-29 Hartmut new: Comment in Datapath supported. For nice presentation in list on long variable paths. 078 * <li>2013-11-19 Hartmut new: {@link #repaint(int, int)} overridden forces paint of the whole curve 079 * by setting {@link #bPaintAllCmd}, whereby in {@link #setSample(float[], int)} super.repaint is invoked, 080 * which does not set {@link #bPaintAllCmd} and paints therefore only the new data. 081 * <li>2013-07-25 Hartmut new AutoSave capabilities. 082 * <li>2013-05-19 Hartmut new: {@link GralCurveViewMouseAction} 083 * <li>2013-05-19 Hartmut new: {@link #selectTrack(int, int, int, int)} with ctrl and left mouse pressed 084 * <li>2013-05-14 Hartmut new: {@link Track#getIxTrack()} 085 * <li>2013-03-27 Hartmut bugfix: The {@link #bNewGetVariables} have to be set in {@link #setDataPath(String)} and 086 * {@link #applySettings(String)} because this methods sets a new variable which should be searched. 087 * All other changes are gardening and comments. 088 * <li>2013-01-25 Hartmut improved: Error message in {@link #refreshFromVariable(VariableContainer_ifc)} only on 089 * paint-complete of the view. If the implementation receives a paintAll command by mouseClick on the graphic 090 * or because the graphic is shown the first time, the {@link #setPaintAllCmd()} is invoked there. The variable 091 * {@link #bNewGetVariables} is set, that causes the error message. 092 * Before: no error message was created, the error was obscure. 093 * <li>2012-08-11 Hartmut now grid with timestamps 094 * <li>2012-06-08 Hartmut new: {@link #applySettings(String)} and {@link #writeSettings(Appendable)} for saving 095 * and getting the configuration of curve view from a file or another text. 096 * <li>2012-04-01 Hartmut new: Using {@link VariableAccessWithIdx} to access values. 097 * <li>2012-03-25 Hartmut chg: Some routines from SWT moved to this because there are independent. 098 * <li>2012-03-17 Hartmut chg: All track-associated data now in the {@link Track} inner class. 099 * <li>2012-02-25 Hartmut new: All data have a short timestamp. The x-pixel are calculated with timestamp, 100 * not only with currently storage. 101 * <li>2012-02-25 Hartmut new: Zoom functinality with timestamp and cursors 102 * <li>2012-02-21 Hartmut Now the CurveView works in the new environment. Some adjustments necessary yet. 103 * <li>2011-06-00 Hartmut New concept of GralWidget etc and new Configuration concept 104 * with {@link org.vishia.gral.cfg.GralCfgBuilder}. The old GuiDialogZbnfControlled.class 105 * was not use nevermore. But the CurveView was not adapted for that. 106 * <li>2010-03-00 Hartmut The curve view was development as basic feature. 107 * </ul> 108 * <br><br> 109 * <b>Copyright/Copyleft</b>: 110 * For this source the LGPL Lesser General Public License, 111 * published by the Free Software Foundation is valid. 112 * It means: 113 * <ol> 114 * <li> You can use this source without any restriction for any desired purpose. 115 * <li> You can redistribute copies of this source to everybody. 116 * <li> Every user of this source, also the user of redistribute copies 117 * with or without payment, must accept this license for further using. 118 * <li> But the LPGL is not appropriate for a whole software product, 119 * if this source is only a part of them. It means, the user 120 * must publish this part of source, 121 * but doesn't need to publish the whole source of the own product. 122 * <li> You can study and modify (improve) this source 123 * for own using or for redistribution, but you have to license the 124 * modified sources likewise under this LGPL Lesser General Public License. 125 * You mustn't delete this Copyright/Copyleft inscription in this source file. 126 * </ol> 127 * If you intent to use this source without publishing its usage, you can get 128 * a second license subscribing a special contract with the author. 129 * 130 * @author Hartmut Schorrig = hartmut.schorrig@vishia.de 131 * 132 */ 133 public final static int version = 20130327; 134 135 136 137 138 public class CommonCurve { 139 140 /**If true, then the display is freezed. 141 */ 142 public boolean bFreeze = false; 143 144 145 /**If set, then use this variable to get the short time. 146 * 147 */ 148 public VariableAccess_ifc timeVariable; 149 150 /**If set, the {@link #timeVariable} will be initialized. 151 * 152 */ 153 public String timeDatapath; 154 155 156 157 } 158 159 160 161 162 163 164 /**Instances of this class were be created only temporary while transfer a parse result of {@link GralCurveView#applySettings(String)} 165 * The instance holds values for {@link ZbnfJavaOutput} to apply to a track 166 * calling {@link GralCurveView#addTrack(String, String, GralColor, int, int, float, float)} 167 * inside {@link GralCurveView.ZbnfSetCurve#add_Track(ZbnfSetTrack)}. 168 */ 169 public static class ZbnfSetTrack { 170 /**From Zbnf component with semantic <?name>. */ 171 public String name; 172 /**From Zbnf component with semantic <?datapath>. */ 173 public String datapath; 174 /**Color and style from Zbnf component with semantic <?color>. */ 175 GralColor color_; int style_ = 0; 176 /**From Zbnf component with semantic <?nullLine>. */ 177 public int nullLine; 178 /**From Zbnf component with semantic <?scale>. */ 179 public float scale; 180 /**From Zbnf component with semantic <?offset>. */ 181 public float offset; 182 /**From Zbnf component with semantic <?color>. */ 183 public void set_color(String color){ color_ = GralColor.getColor(color.trim()); } 184 } 185 186 187 188 /**The instance of this class {@link GralCurveView#zbnfSetCurve} will be used only temporary while transfer a parse result 189 * of {@link GralCurveView#applySettings(String)} 190 * The instance contains only the capabilty to get the track info from the parse result 191 * and to call {@link GralCurveView#addTrack(String, String, GralColor, int, int, float, float)} 192 * inside {@link #add_Track(ZbnfSetTrack)}. 193 */ 194 public class ZbnfSetCurve{ 195 /**From Zbnf component with semantic <?Track>. */ 196 public ZbnfSetTrack new_Track(){ 197 return new ZbnfSetTrack(); 198 } 199 /**From Zbnf component with semantic <?Track>. */ 200 public void add_Track(ZbnfSetTrack track){ 201 addTrack(track.name, track.datapath, track.color_, track.style_, track.nullLine, track.scale, track.offset); 202 } 203 204 public void set_timeDatapath(String val){ 205 GralCurveView.this.common.timeDatapath = val; 206 } 207 208 } 209 210 final ZbnfSetCurve zbnfSetCurve = new ZbnfSetCurve(); 211 212 213 /**Instances of this class contain the scaling for one ore more tracks. 214 * One instance if referenced from maybe one or more as one track. 215 */ 216 public static class TrackScale 217 { 218 /**The scale for 10 percent of view without zoom.. */ 219 public float yScale; 220 221 /**The value of input data which is shown at the 0-line. */ 222 public float yOffset; 223 224 //public float yFactor; 225 226 /**The percent from 0..100 where the 0-line is presented. */ 227 public int y0Line; 228 229 230 @Override public TrackScale clone(){ 231 try{ return (TrackScale)super.clone(); } 232 catch(CloneNotSupportedException exc){ 233 TrackScale ret = new TrackScale(); 234 ret.yScale = this.yScale; 235 ret.yOffset = this.yOffset; 236 ret.y0Line = this.y0Line; 237 return ret; 238 } 239 } 240 } 241 242 243 /**The describing and the actual data of one track (one curve) 244 */ 245 public static class Track implements GralCurveViewTrack_ifc, GralSetValue_ifc { 246 public final String name; 247 248 /**The index of the track in the List of tracks. 249 * It is the correspondent index in the float parameter for {@link GralCurveView#setSample(float[], int)} 250 */ 251 public final int ixList; 252 253 private final GralCurveView outer; 254 255 public String sDataPath; 256 257 public VariableAccess_ifc variable; 258 259 private Object oContent; 260 261 /**Index of a variable 262 * 263 */ 264 private int dataIx; 265 266 public float YYYactValue; 267 268 public float min, max; 269 270 /**Reference to the scaling to show the track. More as one track can build a scale group 271 * which refers the same instance. */ 272 public TrackScale scale; 273 274 /**The color of the line. */ 275 public GralColor lineColor; 276 277 /**The brightness of the line. It is used to show the selected line. */ 278 public int lineWidth = 1; 279 280 281 /**The state to show. 0=hidden, don't show, but the values are stored. 282 * 1= show normal. 2= show lifted out (selected). 283 */ 284 public int showSelected = 1; 285 //public float y0Pix; 286 287 /**Array stores the last values which are able to show. */ 288 public float[] values; 289 290 /**last values for paint. 291 * The current paint goes from lastValueY[1] to the current point. 292 * The lastValueY[0] is used to repaint the last curve peace while shifting draw content. 293 */ 294 protected final int[] XXXlastValueY = new int[2]; 295 296 int identLastSelect; 297 298 /**The value from last draw, do not calculate twice. */ 299 public int ypixLast; 300 301 public Track(GralCurveView outer, String name, int ixList){ 302 this.outer = outer; this.name = name; this.ixList = ixList; } 303 304 @Override public void setContentInfo(Object content) { oContent = content; } 305 306 @Override public Object getContentInfo() { return oContent; } 307 308 @Override public void setDataPath(String sDataPath) { 309 this.sDataPath = sDataPath; 310 this.variable = null; 311 outer.bNewGetVariables= true; //force searching the variable 312 } 313 314 @Override public String getDataPath() { return sDataPath; } 315 316 @Override public int getDataIx() { return dataIx; } 317 318 @Override public void setDataIx(int dataIx) { this.dataIx = dataIx; } 319 320 @Override public void setValue(float value) { this.YYYactValue = value; } 321 322 @Override public void setLongValue(long value) { this.YYYactValue = value; } 323 324 @Override public void setValue(Object[] value) { } //TODO this.actValue = value[0]; } 325 326 /**Maybe used for scaling, yet unused here. 327 * @see org.vishia.gral.ifc.GralSetValue_ifc#setMinMax(float, float) 328 */ 329 @Override public void setMinMax(float minValue, float maxValue) { 330 this.min = minValue; this.max = maxValue; 331 } 332 333 @Override public int getLinePercent(){ return scale.y0Line; } 334 335 @Override public float getOffset(){ return scale.yOffset; } 336 337 @Override public float getScale7div(){ return scale.yScale; } 338 339 @Override public GralColor getLineColor(){ return lineColor; } 340 341 342 /**Change the scaling of a track. 343 * @param trackNr Number of the track in order of creation, 0 ist the first. 344 * @param scale7div value per division 345 * @param offset value, which is shown at line0 346 * @param line0 percent of 0-line in graphic. 347 */ 348 public void setTrackScale(float scale7div, float offset, int line0){ 349 scale.yScale = scale7div; 350 scale.yOffset = offset; 351 scale.y0Line = line0; 352 } 353 354 355 /**Sets the scaling to sharing with another track. Changing one of this tracks with 356 * {@link #setTrackScale(float, float, int)} influences all shared tracks immediately. 357 * All shared tracks refer the same instance of {@link TrackScale}. 358 * @param from 359 */ 360 @Override public void groupTrackScale(GralCurveViewTrack_ifc from){ scale = ((Track)from).scale; } 361 362 @Override public boolean isGroupedTrackScale(GralCurveViewTrack_ifc with){ 363 if(with == null) return false; 364 return scale == ((Track)with).scale; 365 } 366 367 /**Sets the scaling to a own instance of {@link TrackScale}. The track is independent now. */ 368 @Override public void ungroupTrackScale(){ 369 TrackScale oldScale = this.scale; 370 this.scale = oldScale.clone(); 371 } 372 373 @Override public float getValueCursorLeft(){ return getValueCursor(((GraphicImplAccess)outer._wdgImpl).xpCursor1); } 374 375 376 377 @Override public float getValueCursorRight(){ return getValueCursor(((GraphicImplAccess)outer._wdgImpl).xpCursor2); } 378 379 380 private float getValueCursor(int cursor){ 381 float value; /// 382 if(cursor >=0 && cursor < ((GraphicImplAccess)outer._wdgImpl).ixDataShown.length){ 383 try{ 384 int ixData = ((GraphicImplAccess)outer._wdgImpl).getIxDataFromPixelRight(cursor); 385 value = values[ixData]; 386 } catch(Exception exc){ 387 value = 77777.7f; 388 } 389 } else { 390 value = 777777.7f; 391 } 392 return value; 393 } 394 395 396 397 @Override public float getValueLast(){ return 0; } 398 @Override public float getValueMin(){ return 0; } 399 @Override public float getValueMax(){ return 0; } 400 401 402 403 404 405 /** 406 * @see org.vishia.gral.ifc.GralCurveViewTrack_ifc#setLineProperties(org.vishia.gral.ifc.GralColor, int, int) 407 */ 408 @Override public void setLineProperties(GralColor color, int width, int pattern){ 409 if(color !=null) { lineColor = color; } 410 if(width >0) { lineWidth = width;} 411 } 412 413 @Override public void setVisible(int mode){ showSelected = mode; } 414 415 @Override public int getVisible(){ return showSelected; } 416 417 418 419 420 @Override public void setText(CharSequence text){ 421 System.err.println("GralCurveView - setText not supported; Widget = " + name + "; text=" + text); 422 } 423 424 425 426 @Override public String toString(){ return "Track: " + name + "(" + sDataPath + "," + (variable !=null ? variable.toString(): "variable = null") + ")"; } 427 428 } 429 430 /**Inner class for time organisation. */ 431 public static class TimeOrganisation{ 432 433 Timeshort absTime = new Timeshort(); 434 435 /**It is counted while the pair {@link #absTime_short} and {@link #absTime} is set newly. 436 * The pair is consistent only if this counter is the same before and after read. 437 */ 438 private volatile int ctTimeSet; 439 440 /**The last timeshort from the timeVariable. To detect newly simulation if it starts with a lesser value. */ 441 int timeshortLast; 442 443 /**Value to add to the time stamp to get a continuous time for some simulations one after another. */ 444 int timeshortAdd; 445 446 public int lastShortTimeDateInCurve; 447 448 /**Short time stamp of the oldest stored point. */ 449 public int firstShortTimeDateInCurve; 450 451 public int nrofPixelForTimestep; 452 453 /**Division type. 454 * <ul> 455 * <li>'2': 100 2 4 6 8 10 12 14 16 18 120 456 * <li>'1': 100 1 2 3 4 5 6 7 8 9 200 457 * <li>'5': 100 5 10 15 20 25 30 35 40 45 150 458 * </ul> 459 */ 460 //public char divType; 461 462 463 /**Number of pixel per 1 fine division in vertical lines. The time for 1 division is a rounded number. 464 * The number of pixel have to be float, because it is cummulated. */ 465 float pixelPerTimeDiv, pixelPerTimeFineDiv; 466 467 //int divPerBoldDiv = 10; 468 469 /**Number of millisec between 2 fine divisions. It is a number of millisec able to divide by 10, 5 or 2. */ 470 int millisecPerDiv = 100, millisecPerFineDiv =10; 471 472 /**Number of shorttime units for 1 pixel. 473 * A time unit may be 1 millisecond for currently showing curves. 474 * This value may be e.g. 100 to show 30 seconds in 300 pixel or e.g. 10 to show 5 seconds in 500 pixel. 475 * This value may be given in another unit for example 1 microseconds. It should be the same value unit 476 * as used in {@link #setSample(float[], int)}. 477 */ 478 public float timePerPixel = 1.0f; 479 480 481 /**The reciprocal of {@link #timePerPixel}. The number of pixel for 1 short time step. It is less 1.0 often. */ 482 public float pixel7time = 1.0f; 483 484 /**Number of time shortTime steps to showing the curve in the current view. 485 * If this value is given (>=0) then the {@link #timePerPixel} will be calculated from this 486 * regarding the current graphic size. 487 * This value should be given in the same value unit as used for {@link #setSample(float[], int)}. 488 */ 489 public int timeSpread = 50000; 490 491 /// 492 /**The last tested time to produce vertical lines for time division. */ 493 public int timeLeftShowing; 494 495 public long timeAbsOnLastStrongDiv; 496 497 //public String sTimeAbsSec; 498 499 /**Accumulated nr of pixel written with short drawing after a strong division. 500 * It is used to write the number after a proper time. 501 */ 502 public int pixelWrittenAfterStrongDiv; 503 504 /**Pixel position from right for fine divisions of time lines (vertical lines) and normal divisions. 505 * This array is filled newly whenever any draw or paint action is done. It is prepared in the routine 506 * {@link #prepareIndicesDataForDrawing(int, int, int)} and used in the draw routine of the implementation level. 507 */ 508 public final int[] xPixelTimeDiv = new int[50], xPixelTimeDivFine = new int[200]; 509 510 511 /**The text written at the time divisions. It is a mm:ss, ss.SSS or hh::mm */ 512 public final String[] sTimeAbsDiv = new String[50]; 513 514 515 int[] millisecPerFineDivVariants = new int[]{ 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 60000, 120000, 600000, 1200000, 3600000, 18000000}; 516 517 518 519 /**Milliseconds per division for number of pixel per fine division between 12..30. 520 * 521 */ 522 int[] millisecPerDivVariants = new int[]{ 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 60000, 120000, 300000, 600000, 3600000, 7200000, 18000000, 72000000}; 523 524 /**Calculates the divisions with known {@link #timePerPixel}. 525 * This routine should be called whenever the display zoom will be changed. 526 * It sets {@link #divType}, {@link #pixelPerTimeFineDiv}, {@link #millisecPerFineDiv} 527 */ 528 public void calc(){ 529 int millisec20pixel = (int)(12 * timePerPixel * absTime.millisec7short()); //millisec for 12 pixel 530 boolean bFound = false; 531 for(int ii = 0; ii < millisecPerDivVariants.length; ++ii){ //search in [5 10 20 ...] etc. millisecPerDivisions 532 if(millisecPerFineDivVariants[ii] >= millisec20pixel){ 533 millisecPerDiv = millisecPerDivVariants[ii]; //sets millisecPerDiv and ..FineDiv 534 millisecPerFineDiv = millisecPerFineDivVariants[ii]; 535 bFound = true; 536 break; 537 } 538 } 539 if(bFound) { 540 pixelPerTimeDiv = millisecPerDiv / (timePerPixel * absTime.millisec7short()); //number of pixel per division, should be appropriate 48..150 541 pixelPerTimeFineDiv = millisecPerFineDiv / (timePerPixel * absTime.millisec7short()); //number of pixel per division, should be appropriate 12..30 542 } else { 543 //use a fix value to prevent any failure calculations. 544 pixelPerTimeDiv = 150; 545 pixelPerTimeFineDiv = 30; 546 } 547 /* 548 double millisecExp20pixel = Math.log10(millisec20pixel); 549 int millisecExpInt20pixel = (int)millisecExp20pixel; 550 int milli10sec = (int)Math.pow(10, millisecExpInt20pixel); 551 float pixelMilli10sec = milli10sec / timePerPixel; 552 if(pixelMilli10sec < 4){ 553 divType = '5'; 554 pixelPerTimeFineDiv = 5 * pixelMilli10sec; 555 millisecPerFineDiv = milli10sec * 5; 556 } else if(pixelMilli10sec < 10){ 557 divType = '2'; 558 pixelPerTimeFineDiv = 2 * pixelMilli10sec; 559 millisecPerFineDiv = milli10sec * 2; 560 } else{ //10..20 561 divType = '1'; 562 pixelPerTimeFineDiv = pixelMilli10sec; 563 millisecPerFineDiv = milli10sec; 564 } 565 if(millisecPerFineDiv <=2000 ){ 566 millisecPerDiv = 10 * millisecPerFineDiv; //up to 10 sec step 567 pixelPerTimeDiv = 10 * pixelPerTimeFineDiv; 568 } else if(millisecPerFineDiv <=20000 ){ 569 millisecPerDiv = 6 * millisecPerFineDiv; //up to 10 sec step 570 pixelPerTimeDiv = 6 * pixelPerTimeFineDiv; 571 } else { 572 millisecPerDiv = 10 * millisecPerFineDiv; //minute steps. 573 pixelPerTimeDiv = 10 * pixelPerTimeFineDiv; 574 } 575 */ 576 } 577 578 } 579 580 public TimeOrganisation timeorg = new TimeOrganisation(); 581 582 583 protected static class DataOrganisation 584 { 585 /**Index in the {@link Track#values} where the last auto write was done. 586 * See {@link GralCurveView#timeInitAutoSave()}. */ 587 public int ixDataStartAutoSave, ixDataEndAutoSave; 588 589 590 public int ixDataStartSave, ixDataEndSave; 591 592 /**The number of array elements which are used in {@link #ixDataShown} for the current view. 593 * Either the display size is less, then this is less then 2000. It is the usual case. 594 * Or the number of available data are less, the it is lesser than the pixel size of the curve. 595 */ 596 protected int zPixelDataShown; 597 598 599 /**Set to true if the data are wrapped in the buffer. If false, there are less data. */ 600 boolean bWrappedInBuffer; 601 602 } 603 604 protected final DataOrganisation dataOrg = new DataOrganisation(); 605 606 607 608 protected static class SaveOrganisation{ 609 610 /** Values to save for autosave. */ 611 public int nrofValuesAutoSave; 612 613 614 public int ctValuesAutoSave; 615 616 //boolean bAutoSave; 617 } 618 619 protected SaveOrganisation saveOrg = new SaveOrganisation(); 620 621 622 623 624 public static class PixelOrganisation 625 { 626 /**Size of the curve range in pixel. Set at any time on last draw action. */ 627 public int xPixelCurve, yPixelCurve; 628 629 } 630 631 /**All tracks to return for filling. It has the same members in the same order like {@link #listTracks}*/ 632 //protected final List<GralSetValue_ifc> listTrackSet = new LinkedList<GralSetValue_ifc>(); 633 634 635 public final CommonCurve common; 636 637 638 /**True then saves values. */ 639 public boolean bActive; 640 641 642 /**Current number of values in the data field. 643 * If less values are set ({@link #setSample(float[])}, 644 * then the nrofValues is less than values.length. 645 * Else it is ==values.length. */ 646 public int nrofValues = 0; 647 648 649 private boolean bNewGetVariables = true; 650 651 652 /**A short timestamp for the values in {@link GralCurveView.Track#values}. 653 * It maybe a millisecond timestamp. Then about 2000000 seconds are able to display. 654 * This array and the {@link Track#actValue} array are filled wrapping. 655 * <br><br> 656 * A negative difference between 2 successive time stamps designates the time break point between newer values 657 * and the successive currently points to the newest one in ascending direction (past to future). 658 * Initially this array will be filled with successive values from 0 to negatives in step -1 659 * to designate a time break at all points. 660 * <br><br> 661 * At startup of filling 3 data points this array is filled for example with 662 * <pre> 663 * -1234, -1245, -1256, -3, -4, -5, ... 664 * </pre> 665 * In this example the wrapping time stamps are negative but successive ascending of course. 666 * The not used 4. point is accepted as a correct point faulty, it produces a presentation to the value 0 667 * because nothing is stored in data in 1253 time units (which may be milliseconds). This faulty presentation 668 * is only present on startup of graphic and only if the difference is in range of presentation width, 669 * see {@link #prepareIndicesDataForDrawing(int, int)} 670 */ 671 public final int[] timeValues; 672 673 674 /**Distance of nrofValues for one vertical strong grid line. 675 * This value is used to limit nrofValuesForGrid. */ 676 public int gridDistanceStrongY; 677 678 679 /**Deepness of the storage of values to present. 680 * It is a power of 2 anytime. 681 */ 682 public final int maxNrofXValues; 683 684 685 public boolean testStopWr; 686 687 688 /**Write index of values, increment by {@link #setSample(float[], int)}. 689 * The index refers to the last written value. 690 * Initially it is -1, the first write will be increment ?? to prevent any exception while values are not set before. 691 */ 692 public int ixDataWr; 693 694 695 /**All tracks to return for filling. It has the same members in the same order like {@link #listTracks}*/ 696 //protected final List<GralSetValue_ifc> listTrackSet = new LinkedList<GralSetValue_ifc>(); 697 698 699 /**The number of shift right to get the numeric index in values. */ 700 public final int shIxiData; 701 702 703 /**Mask of index in values. */ 704 public final int mIxiData; 705 706 707 /**Mask of any ixData. */ 708 protected final int mIxData; 709 710 711 public int newSamples; 712 713 714 protected int nrofValuesForGrid; 715 716 717 /**The increment step of ixData. 718 * Concept of wrap around: The values[]-array are used wrap around, to prevent 719 * expensive shift operations. The size of the array is a power of 2 anytime. 720 * The index is hold in an integer variable, which wraps around in the bit space too. 721 * To use cheap increment and compare functionality, the range for the index 722 * is the full integer range. To step from one to next index, use this adding value. 723 */ 724 public final int adIxData; 725 726 727 /**All tracks. Anytime if the tracks are changed a new List is created. It is because the list can be used in another thread yet in an iterator. */ 728 public List<Track> listTracks = new ArrayList<Track>(); 729 730 731 protected final GralCurveViewMouseAction mouseAction = new GralCurveViewMouseAction(); 732 733 734 /**This action is called whenever a cursor position is changed. */ 735 private GralUserAction actionMoveCursor; 736 737 738 /**This action is called whenever a track was selected. */ 739 private GralUserAction actionSelectTrack; 740 741 742 743 744 745 public GralCurveView(String sName, int maxNrofXvaluesP, CommonCurve common) 746 { 747 super(null, sName, 'c'); 748 this.common = common == null ? new CommonCurve() : common; 749 int maxNrofXvalues1 = 1; 750 int shIxData1 = 32; 751 while(maxNrofXvalues1 < maxNrofXvaluesP){ 752 maxNrofXvalues1 <<=1; 753 shIxData1 -=1; 754 } 755 this.shIxiData = shIxData1; 756 this.maxNrofXValues = maxNrofXvalues1; //maxNrofXvalues; 757 this.adIxData = 0x40000000 / (maxNrofXValues >>2); //NOTE: integer division, all >>2 758 this.mIxData = ~(this.adIxData -1); //all bits which have to be used, mask out lower bits. 759 this.mIxiData = maxNrofXValues -1; //e.g. from 0x1000 to 0xfff 760 this.ixDataWr = -adIxData; //initial write position, first increment to 0. 761 // 762 timeValues = new int[maxNrofXValues]; 763 cleanBuffer(); 764 saveOrg.nrofValuesAutoSave = (int)(maxNrofXValues * 0.75); 765 //values = new float[maxNrofXvalues][nrofTracks]; 766 //setPanelMng(mng); 767 setActionMouse(mouseAction, 0); 768 769 //mng.registerWidget(this); 770 } 771 772 773 774 public void cleanBuffer() 775 { 776 for(int ix = 0; ix < maxNrofXValues; ++ix){ 777 timeValues[ix] = ix; //store succession of time values to designate it as empty. 778 } 779 timeorg.calc(); 780 timeorg.timeshortAdd = 0; 781 timeorg.timeshortLast = 0; 782 timeorg.absTime.clean(); 783 if(super._wdgImpl !=null) { 784 GraphicImplAccess wdgi = (GraphicImplAccess)super._wdgImpl; 785 wdgi.ixDataDraw = ixDataWr =0; 786 wdgi.ixDataCursor1 = wdgi.ixDataCursor2 = 0; 787 wdgi.ixDataShowRight = 0; 788 Arrays.fill(wdgi.ixDataShown, 0); 789 } 790 } 791 792 793 794 795 /**It will be called after construction of the implementation graphic in the derived ctor. 796 * 797 */ 798 public void initMenuContext(){ 799 GralMenu menuCurve = getContextMenu(); 800 //menuCurve.addMenuItemGthread("pause", "pause", null); 801 menuCurve.addMenuItem("refresh", actionPaintAll); 802 menuCurve.addMenuItem("go", actionGo); 803 //menuCurve.addMenuItemGthread("zoomOut", "zoom in", null); 804 menuCurve.addMenuItem("zoomBetweenCursor", "zoom between Cursors", actionZoomBetweenCursors); 805 menuCurve.addMenuItem("zoomOut", "zoom out", actionZoomOut); 806 menuCurve.addMenuItem("cleanBuffer", "clean Buffer", actionCleanBuffer); 807 //menuCurve.addMenuItemGthread("zoomOut", "to left", null); 808 //menuCurve.addMenuItemGthread("zoomOut", "to right", null); 809 810 } 811 812 813 public CommonCurve getCommonData(){ return common; } 814 815 /**This action will be called whenever a cursor position is changed. */ 816 public void setActionMoveCursor(GralUserAction action){ actionMoveCursor = action; } 817 818 819 /**This action will be called whenever a track was selected. The selection will be done by 820 * pressing the left mouse with ctrl on a curve view point. */ 821 public void setActionTrackSelected(GralUserAction action){ actionSelectTrack = action; } 822 823 824 /**Creates and adds a track of the curve view. Note: The current {@link #listTracks} instance won't be changed. 825 * Instead, a new ArrayList is created here with the content of listTracks and the new track. 826 * This is because the {@link #listTracks} instance may be used in a iterator in another thread. 827 * That is in {@link #setSample(float[], int)} which can be invoked in a cyclically or receiving thread, 828 * or in {@link #refreshFromVariable(VariableContainer_ifc)} which is invoked in the Inspector Gui cyclically 829 * to get values from target. If during operation a track was add by the users operation on GUI, it should not disturb the current process. 830 * 831 * 832 * This routine should be called only one time per track to create a new one. 833 * in the order of tracks. 834 * Note: renamed from initTrack, 2018-09 835 * @param sNameTrack 836 * @param sDataPath 837 * @param color 838 * @param style 839 * @param nullLine 840 * @param scale 841 * @param offset 842 * @return The track instance. 843 */ 844 public Track addTrack(String sNameTrack, String sDataPath, GralColor color, int style 845 , int nullLine, float scale, float offset) 846 { 847 //Track track = initTrack(sNameTrack, sDataPath, color, style, nullLine, scale, offset, listTracksNew); 848 Track track = new Track(this, sNameTrack, listTracks.size()); 849 track.values = new float[this.maxNrofXValues]; 850 track.scale = new TrackScale(); 851 track.sDataPath =sDataPath; 852 track.variable = null; 853 track.scale.y0Line = nullLine; 854 track.scale.yOffset = offset; 855 track.scale.yScale = scale; 856 track.lineColor = color == null ? GralColor.getColor("rd") : color; 857 bNewGetVariables = true; //to force re-read of all variables. 858 // 859 //add the track, but build a new List 860 List<Track> listTracksNew = new ArrayList<Track>(); 861 listTracksNew.addAll(this.listTracks); //Atomic access, iterate in local referenced list. 862 listTracksNew.add(track); 863 listTracks = listTracksNew; 864 return track; 865 } 866 867 868 869 870 871 872 /**Searches and returns the track from listTrack per name. 873 * @param name 874 * @return null if not found. 875 */ 876 private Track searchTrack(String name) { 877 for(Track e: listTracks) { 878 if(e.sDataPath.equals(name)) return e; 879 } 880 return null; 881 } 882 883 884 885 public void setTimePerPixel(int time){ 886 timeorg.timePerPixel = time; 887 } 888 889 890 public void setTimeSpread(int time){ 891 if(time <= 0) throw new IllegalArgumentException("GralCurveView.setTimeSpread - value should >0"); 892 timeorg.timeSpread = time; 893 } 894 895 896 897 /**Returns the path of the time variable if given or null. 898 */ 899 public String getTimeVariable(){ return common.timeDatapath; } 900 901 902 /**This list describes the data paths in that order, which should be regard 903 * calling {@link #setSample(float[])}. 904 * return a new List which should not modify. Modify has no sense because the list is not used inside this class. 905 */ 906 public List<GralSetValue_ifc> getTracks(){ 907 List<GralSetValue_ifc> ret = new LinkedList<GralSetValue_ifc>(); 908 List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list. 909 for(Track track : listTracks1){ 910 ret.add(track); 911 } 912 return ret; 913 } 914 915 916 /**This Iterable describes the data paths in that order, which should be regard 917 * calling {@link #setSample(float[])}. 918 */ 919 public Iterable<? extends GralCurveViewTrack_ifc> getTrackInfo(){ return listTracks; } 920 921 922 923 /**Returns that track which was selected by set cursor at last. 924 * @return null if a track was not selected up to now, elsewhere one of the created tracks. 925 */ 926 public Track getTrackSelected(){ return _wdgImpl == null ? null : ((GraphicImplAccess)_wdgImpl).trackSelected; } 927 928 929 public boolean shouldAutosave(){ 930 return saveOrg.ctValuesAutoSave >= saveOrg.nrofValuesAutoSave; 931 } 932 933 934 public long timeRight(){ 935 int ixData = ((GraphicImplAccess)super._wdgImpl).ixDataShown[0]; //right 936 int timeShort1 = timeValues[(ixData >> shIxiData) & mIxiData]; 937 //synchronized() 938 return timeorg.absTime.absTimeshort(timeShort1); 939 940 } 941 942 943 944 @Override public void setTimePoint(long date, int timeshort, float millisecPerTimeshort){ 945 timeorg.absTime.setTimePoint(date, timeshort, millisecPerTimeshort); 946 } 947 948 949 /** 950 * @see org.vishia.gral.ifc.GralCurveView_ifc#setSample(float[], int) 951 */ 952 @Override public void setSample(float[] values, int timeshort) { 953 if(testStopWr) return; //only for debug test. 954 //if(++ixDataWr >= maxNrofXValues){ ixDataWr = 0; } //wrap arround. 955 if( ++saveOrg.ctValuesAutoSave > saveOrg.nrofValuesAutoSave) { 956 saveOrg.ctValuesAutoSave = saveOrg.nrofValuesAutoSave; //no more. 957 } 958 ixDataWr += adIxData; /// 959 if(ixDataWr == -adIxData ){ //store to the last position in the data array 960 dataOrg.bWrappedInBuffer = true; 961 } 962 if(!common.bFreeze && super._wdgImpl !=null){ 963 ((GraphicImplAccess)super._wdgImpl).ixDataShowRight = ixDataWr; 964 } 965 this.newSamples +=1; //information for paint event 966 this.nrofValuesForGrid +=1; 967 if(nrofValuesForGrid > maxNrofXValues + gridDistanceStrongY){ 968 nrofValuesForGrid -= gridDistanceStrongY; //prevent large overflow. 969 } 970 int ixSource = -1; 971 int ixWr = (ixDataWr >> shIxiData) & mIxiData; 972 // 973 // 974 //assign the argument values to the track.values 975 // 976 for(Track track: listTracks){ 977 if(ixSource < values.length -1){ 978 float val = values[++ixSource]; //write in the values. 979 track.values[ixWr] = val; //write in the values. 980 if(track.min > val ){ track.min = val; } 981 if(track.max < val ){ track.max = val; } 982 } else { 983 track.values[ixWr] = 0; 984 } 985 } 986 // 987 int timeLast = timeValues[ixWr]; 988 timeValues[ixWr] = timeshort; 989 990 timeorg.lastShortTimeDateInCurve = timeshort; 991 if(nrofValues < maxNrofXValues){ 992 if(nrofValues ==0){ 993 timeorg.firstShortTimeDateInCurve = timeshort; 994 } 995 this.nrofValues +=1; 996 } else { 997 timeorg.firstShortTimeDateInCurve = timeLast; 998 if(super._wdgImpl !=null) ((GraphicImplAccess)(super._wdgImpl)).nrofDataShift.incrementAndGet(); //shift data in graphic. 999 } 1000 1001 if(!common.bFreeze){ 1002 synchronized(this){ 1003 if(super._wdgImpl !=null) ((GraphicImplAccess)(super._wdgImpl)).redrawBecauseNewData = true; 1004 } 1005 super.repaint(50,100); 1006 } 1007 } 1008 1009 1010 1011 /** 1012 * @see org.vishia.gral.base.GralWidget#refreshFromVariable(org.vishia.byteData.VariableContainer_ifc) 1013 */ 1014 @Override public void refreshFromVariable(VariableContainer_ifc container){ 1015 if(bActive){ 1016 List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list. 1017 float[] values = new float[listTracks1.size()]; 1018 int ixTrack = -1; 1019 boolean bRefreshed = false; //set to true if at least one variable is refreshed. 1020 for(Track track: listTracks1){ 1021 if(track.variable ==null && bNewGetVariables){ //no variable known, get it. 1022 String sDataPath = track.getDataPath(); 1023 if(sDataPath !=null){ 1024 if(sDataPath.startsWith("-")){ //it is a comment 1025 int posEnd = sDataPath.indexOf('-',1) +1; 1026 sDataPath = sDataPath.substring(posEnd); //after second '-', inclusive first '-' if no second found. 1027 } 1028 String sPath2 = sDataPath.trim(); 1029 if(!sDataPath.startsWith("#")){ //don't regard commented line 1030 String sPath = itsMng.getReplacerAlias().replaceDataPathPrefix(sPath2); //replaces only the alias: 1031 track.variable = container.getVariable(sPath); 1032 if(track.variable == null){ 1033 System.err.printf("GralCurveView - variable not found; %s in curveview: %s\n", sPath, super.name); 1034 } 1035 } 1036 } 1037 } 1038 final float value; 1039 1040 if(track.variable !=null ){ 1041 if(track.variable.isRefreshed()){ 1042 bRefreshed = true; 1043 } 1044 track.variable.requestValue(); 1045 if(track.getDataPath().startsWith("xxx:")) 1046 stop(); 1047 value = track.variable.getFloat(); 1048 track.variable.requestValue(System.currentTimeMillis()); 1049 } else { 1050 value = 0; 1051 } 1052 values[++ixTrack] = value; 1053 } 1054 bNewGetVariables = false; 1055 final long timeyet = System.currentTimeMillis(); 1056 int timeshort; 1057 if(this.common.timeDatapath !=null && this.common.timeVariable ==null){ 1058 String sPath = itsMng.getReplacerAlias().replaceDataPathPrefix(this.common.timeDatapath); //replaces only the alias: 1059 this.common.timeVariable = container.getVariable(sPath); 1060 } 1061 if(this.common.timeVariable !=null){ 1062 //the time variable should contain a relative time stamp. It is the short time. 1063 //Usual it is in 1 ms-step. To use another step width, a mechanism in necessary. 1064 //1 ms in 32 bit are ca. 2000000 seconds. 1065 timeshort = this.common.timeVariable.getInt() + this.timeorg.timeshortAdd; 1066 this.common.timeVariable.requestValue(timeyet); 1067 if(this.timeorg.absTime.isCleaned()) { 1068 setTimePoint(timeyet, timeshort, 1.0f); //the first time set. 1069 } 1070 else if((timeshort - this.timeorg.timeshortLast) <0 || this.timeorg.absTime.isCleaned()) { 1071 //new simulation time: 1072 int timeshortAdd = this.timeorg.absTime.timeshort4abstime(timeyet); 1073 timeshort += timeshortAdd; 1074 this.timeorg.timeshortAdd += timeshortAdd; 1075 setTimePoint(timeyet, timeshort, 1.0f); //for later times set the timePoint newly to keep actual. 1076 } 1077 } else { 1078 timeshort = (int)timeyet; //The milliseconds from absolute time. 1079 setTimePoint(timeyet, timeshort, 1.0f); //set always a timePoint if not data time is given. 1080 } 1081 if(bRefreshed && timeshort != this.timeorg.timeshortLast) { 1082 //don't write points with the same time, ignore seconds. 1083 setSample(values, timeshort); 1084 this.timeorg.timeshortLast = timeshort; 1085 } 1086 } 1087 } 1088 1089 1090 1091 @Override 1092 public GralColor setBackgroundColor(GralColor color) 1093 { 1094 // TODO Auto-generated method stub 1095 return null; 1096 } 1097 1098 @Override 1099 public GralColor setForegroundColor(GralColor color) 1100 { 1101 // TODO Auto-generated method stub 1102 return null; 1103 } 1104 1105 1106 @Override public void setBoundsPixel(int x, int y, int dx, int dy) 1107 { //widgetSwt.setBounds(x,y,dx,dy); 1108 } 1109 1110 @Override public void activate(boolean activate){ bActive = activate; } 1111 1112 @Override public boolean isActiv(){ return bActive; } 1113 1114 @Override public boolean isFreezed(){ return common.bFreeze; } 1115 1116 1117 1118 1119 1120 /* (non-Javadoc) 1121 * @see org.vishia.gral.ifc.GralCurveView_ifc#applySettings(java.lang.String) 1122 */ 1123 @Override public boolean applySettings(String in){ 1124 boolean bOk; 1125 try{ 1126 //variate syntax in test... 1127 Report console = new ReportWrapperLog(itsMng.log()); 1128 ZbnfParser parser = new ZbnfParser(console); 1129 parser.setSyntax(syntaxSettings); 1130 bOk = parser.parse(in); 1131 if(!bOk){ 1132 console.writeError(parser.getSyntaxErrorReport()); 1133 } else { 1134 this.common.timeDatapath = null; 1135 this.common.timeVariable = null; 1136 listTracks = new ArrayList<Track>(); 1137 //listTrackSet.clear(); 1138 ZbnfJavaOutput setData = new ZbnfJavaOutput(console); 1139 setData.setContent(zbnfSetCurve.getClass(), zbnfSetCurve, parser.getFirstParseResult()); 1140 bNewGetVariables = true; //to force searching variables. 1141 } 1142 } catch(Exception exc){ 1143 System.err.println("GralCurveView.writeSettings() - unexpected IOException;" + exc.getMessage()); 1144 bOk = false; 1145 } 1146 return bOk; 1147 } 1148 1149 1150 1151 @Override public void writeSettings(Appendable out){ 1152 List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list. 1153 for(Track track: listTracks1){ 1154 if(track.sDataPath !=null){ 1155 try{ 1156 out.append("track ").append(track.name).append(":"); 1157 out.append(" datapath=").append(track.sDataPath); 1158 out.append(", color=").append(track.lineColor.toString()); 1159 out.append(", scale=").append(Float.toString(track.scale.yScale) ); 1160 out.append(", offset=").append(Float.toString(track.scale.yOffset)); 1161 out.append(", 0-line-percent=").append(Integer.toString(track.scale.y0Line)); 1162 out.append(";\n"); 1163 } catch(IOException exc){ 1164 System.err.println("GralCurveView.writeSettings() - unexpected IOException;" + exc.getMessage()); 1165 } 1166 } 1167 } 1168 } 1169 1170 1171 public CharSequence timeInitSaveViewArea(){ 1172 int ixData; 1173 if(super._wdgImpl ==null) return "--no graphic--"; 1174 dataOrg.ixDataStartSave = ((GraphicImplAccess)super._wdgImpl).ixDataShown[dataOrg.zPixelDataShown-1]; 1175 int timeShort1 = timeValues[(dataOrg.ixDataStartSave >> shIxiData) & mIxiData]; 1176 dataOrg.ixDataEndSave = ((GraphicImplAccess)super._wdgImpl).ixDataShown[0]; 1177 if(!common.bFreeze){ 1178 //running curve, autosave starts after it. 1179 dataOrg.ixDataEndAutoSave = dataOrg.ixDataEndSave; //atomic access, the actual write pointer. 1180 saveOrg.ctValuesAutoSave = 0; 1181 } 1182 return buildDate(dataOrg.ixDataStartSave, dataOrg.ixDataEndSave); 1183 //return timeorg.absTime.absTimeshort(timeShort1); 1184 } 1185 1186 1187 1188 1189 @Override public CharSequence timeInitAutoSave(){ 1190 dataOrg.ixDataStartAutoSave = dataOrg.ixDataEndAutoSave; //from last end, now start. 1191 //threadsafe: 1192 dataOrg.ixDataEndAutoSave = ixDataWr; //atomic access, the actual write pointer. 1193 saveOrg.ctValuesAutoSave = 0; //maybe non-threadsafe, counts only the time for next save. 1194 return buildDate(dataOrg.ixDataStartAutoSave, dataOrg.ixDataEndAutoSave); 1195 // 1196 //int timeShort1 = timeValues[(dataOrg.ixDataStartAutoSave >> shIxiData) & mIxiData]; 1197 //return timeorg.absTime.absTimeshort(timeShort1); //The appropriate start time. 1198 1199 } 1200 1201 1202 1203 1204 private CharSequence buildDate(int ixStart, int ixEnd){ 1205 int timeShortStart = timeValues[(ixStart >> shIxiData) & mIxiData]; 1206 int timeShortEnd = timeValues[(ixEnd >> shIxiData) & mIxiData]; 1207 long timeStart = timeorg.absTime.absTimeshort(timeShortStart); 1208 long timeEnd = timeorg.absTime.absTimeshort(timeShortEnd); 1209 DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); 1210 DateFormat formatEnd = new SimpleDateFormat("_HH-mm-ss"); 1211 String sNameFile = format.format(new Date(timeStart)) + "_to" + formatEnd.format(new Date(timeEnd)); 1212 return sNameFile; 1213 } 1214 1215 1216 1217 1218 1219 /**Reads a curve /// 1220 * 1221 */ 1222 public void readCurve(File file) 1223 throws IOException 1224 { 1225 InputStream ifile = new FileInputStream(file); 1226 //read first bytes to detect format 1227 byte[] buffer = new byte[256]; 1228 int zBytes = ifile.read(buffer); 1229 ifile.close(); 1230 String firstLine = new String(buffer,0, zBytes); 1231 readCurveCsvHeadline(file); 1232 //timeLastSave = System.currentTimeMillis(); 1233 1234 } 1235 1236 1237 1238 private void readCurveCsvHeadline(File file) 1239 throws IOException 1240 { 1241 String sLine; 1242 BufferedReader ifile = new BufferedReader(new FileReader(file)); 1243 //first head line 1244 setTimePoint(System.currentTimeMillis(), 0, 0.156f); 1245 //line with channels 1246 sLine = ifile.readLine(); 1247 //List<Track> listTracksNew = new ArrayList<Track>(); //// 1248 //listTrackSet.clear(); 1249 int ixInList = -1; 1250 String[] signals = sLine.split(";"); 1251 int[] dataIx = new int[signals.length +10]; 1252 String[] colors = {"bk","rd","gn","bl","or","rd1","rd2","gn1","gn2","bk","rd","gn","bl","or","rd1","rd2","gn1"}; 1253 int ix = -1; 1254 int ixColor = 0; 1255 boolean first_timesh = false; 1256 for(String signal1: signals){ 1257 String signal = signal1.trim(); 1258 if(signal.equals("timesh") && ix == -1) { 1259 first_timesh = true; 1260 } else { 1261 Track track = searchTrack(signal); 1262 if(track !=null) { 1263 //listTracksNew.add(track); 1264 } else { 1265 GralColor color = GralColor.getColor(colors[ixColor]); 1266 track = addTrack(signal, signal, color, 1, 50, 1.0f, 0.0f); //, listTracksNew); 1267 if(ixColor < colors.length-1) {ixColor +=1;} 1268 } 1269 dataIx[++ix] = track.ixList; //getDataIx(); 1270 } 1271 } 1272 //listTracks = listTracksNew; 1273 this.ixDataWr = -adIxData; //initial write position, first increment to 0. 1274 float[] fvalues = new float[listTracks.size() + 10]; 1275 int timeshort = 0; 1276 while( (sLine = ifile.readLine())!=null){ 1277 timeshort +=1; 1278 String[] values = sLine.split(";"); 1279 int ixCol = -1; 1280 boolean bFirstCol = true; 1281 for(String value1: values){ 1282 String sValue = value1.trim(); 1283 sValue = sValue.replace(',', '.'); //accept , as fractional separator, convert to . 1284 if(first_timesh && bFirstCol) { 1285 try{ timeshort = Integer.valueOf(sValue); } 1286 catch(NumberFormatException exc) { 1287 timeshort +=1; //use incremented 1288 } 1289 } else { 1290 float value; 1291 try{ value = Float.valueOf(sValue); } 1292 catch(NumberFormatException exc){ 1293 value = 0.099999999f; 1294 } 1295 int ixData = dataIx[++ixCol]; 1296 fvalues[ixData] = value; 1297 } 1298 bFirstCol = false; 1299 } 1300 setSample(fvalues, timeshort); 1301 } 1302 ifile.close(); 1303 } 1304 1305 1306 1307 1308 1309 /* (non-Javadoc) Writes the curve to the given interface, it is an exporter class. 1310 * @see org.vishia.gral.ifc.GralCurveView_ifc#writeCurve(org.vishia.curves.WriteCurve_ifc, org.vishia.gral.ifc.GralCurveView_ifc.ModeWrite) 1311 */ 1312 @Override public void writeCurve(WriteCurve_ifc out, ModeWrite mode){ 1313 //int ctValues = this.nrofValues -1; //read first, may be increment in next step 1314 int ixDataStart, ixDataEnd; 1315 switch(mode){ 1316 case currentView:{ 1317 ixDataEnd = dataOrg.ixDataEndSave; 1318 ixDataStart = dataOrg.ixDataStartSave; 1319 /* 1320 int xRight = 0; //xpCursor2 1321 //This operation should be done in the graphic thread because ixDataShown is volatile. 1322 int xLeft = dataOrg.xPixelCurve-1; //xpCursor1 1323 if(xLeft >=0 && xLeft < ixDataShown.length){ 1324 ixDataStart = ixDataShown[xLeft]; 1325 } else { 1326 ixDataStart = ixDataWr - (ctValues << shIxiData); //read only one time, the index start from. 1327 } 1328 if(xRight >=0 && xRight < ixDataShown.length){ 1329 ixDataEnd = ixDataShown[xRight]; 1330 } else { 1331 ixDataEnd = ixDataWr; //never used. 1332 } 1333 */ 1334 } break; 1335 case autoSave: { //NOTE: values are set in timeInitAutoSave() 1336 ixDataEnd = dataOrg.ixDataEndAutoSave; 1337 ixDataStart = dataOrg.ixDataStartAutoSave; 1338 }break; 1339 default: 1340 ixDataStart = ixDataEnd = 0; 1341 } 1342 //todo in another thread! 1343 writeCurve(out, ixDataStart, ixDataEnd); 1344 } 1345 1346 1347 1348 1349 1350 1351 /**Writes. 1352 * @param out 1353 * @param ixDataStart 32-bit-wrapping index 1354 * @param ixDataEnd 1355 */ 1356 private void writeCurve(WriteCurve_ifc out, int ixDataStart, int ixDataEnd) 1357 //throws IOException 1358 { boolean bOk = true; 1359 try{ 1360 out.writeCurveTimestamp(new Timeshort(timeorg.absTime)); 1361 if(!bOk){ 1362 out.writeCurveError("absolute time error"); 1363 } else { 1364 int ixTrack = -1; 1365 List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list. 1366 int nrofTracks = listTracks1.size(); 1367 for(Track track: listTracks1){ 1368 String sName = track.name; 1369 String sPath = track.getDataPath(); 1370 GralColor color = track.getLineColor(); 1371 String sColor = color.getColorName(); 1372 out.setTrackInfo(nrofTracks, ++ixTrack, sPath, sName, sColor, track.getScale7div(), track.getOffset(), track.getLinePercent()); 1373 } 1374 float[] record = new float[listTracks1.size()]; 1375 int ix = (ixDataStart >> shIxiData) & mIxiData; 1376 int timeshortLast = timeValues[ix]; 1377 out.writeCurveStart(timeshortLast); 1378 int ixData = ixDataStart; 1379 int ctValues = this.nrofValues -1; //read first, may be increment in next step 1380 while((ixData != ixDataEnd && --ctValues >=0)){ 1381 ix = (ixData >> shIxiData) & mIxiData; 1382 ixTrack = -1; 1383 for(Track track: listTracks1){ 1384 record[++ixTrack] = track.values[ix]; 1385 } 1386 int timeshort = timeValues[ix]; 1387 if((timeshort - timeshortLast)<0){ 1388 //This is a older value since the last one, 1389 //it means it is the first value, all others are overwritten. 1390 out.writeCurveStart(timeshort); 1391 } 1392 out.writeCurveRecord(timeshort, record); 1393 timeshortLast = timeshort; 1394 ixData += adIxData; 1395 } 1396 out.writeCurveFinish(); 1397 } 1398 }catch(IOException exc){ 1399 System.err.println(Assert.exceptionInfo("GralCurveView- exception", exc, 0, 4)); 1400 } 1401 } 1402 1403 1404 /**Forces repaint of the whole curves. It sets the internal flag {@link #bPaintAllCmd} which is checked 1405 * in the paint event routine. In opposite {@link #setSample(float[], int)} invokes 1406 * super.repaint of {@link GralWidget#repaint(int, int)} without setting that flag. Therefore the graphic 1407 * will be shifted to left only with paint of only the new data. 1408 * @see org.vishia.gral.base.GralWidget#repaint(int, int) 1409 */ 1410 @Override public void repaint(int delay, int latest){ 1411 System.out.println("GralCurveView.Info - repaint all Trigger;"); 1412 if(_wdgImpl == null) return; 1413 ((GraphicImplAccess)_wdgImpl).bPaintAllCmd = true; //used in implementation level to force a paint of the whole curves. 1414 bNewGetVariables= true; //used to get faulty variables newly with an error message. 1415 super.repaint(delay, latest); 1416 } 1417 1418 1419 1420 1421 1422 1423 1424 /**All tracks to return for filling. It has the same members in the same order like {@link #listTracks}*/ 1425 //protected final List<GralSetValue_ifc> listTrackSet = new LinkedList<GralSetValue_ifc>(); 1426 1427 1428 public abstract static class GraphicImplAccess extends GralWidget.ImplAccess 1429 implements GralWidgImpl_ifc, Removeable 1430 { 1431 protected final GralCurveView widgg; 1432 1433 protected final PixelOrganisation pixelOrg = new PixelOrganisation(); 1434 1435 /**The track which is selected by the last setCursor. */ 1436 protected Track trackSelected; 1437 1438 /**The number of pixel for the current data point from the current pixel to left. 1439 * [0] is the number of pixel for the right point. 1440 * This array is parallel to {@link #ixDataShown}. 1441 */ 1442 protected final int[] nrofPixel4data = new int[2000]; 1443 1444 /**The index in data for each shown pixel, from right to left int-wrapping. 1445 * [0] is right. Use <code>(ixData >> shIxiData) & mIxiData</code> to calculate the real data index. 1446 * the used length is the number of pixel. 2000 are enough for a large representation. 1447 * This array is filled newly whenever any draw or paint action is done. It is prepared in the routine 1448 * The field contains old indices if the size of drawing is less then the size of window. 1449 * {@link #prepareIndicesDataForDrawing(int, int, int)} and used in the drawTrack routine of the implementation level. 1450 */ 1451 protected final int[] ixDataShown = new int[2000]; 1452 1453 /**The index to show values, it increments with ixWrValues 1454 * if bFreeze is false 1455 */ 1456 protected int ixDataShowRight = 0; 1457 1458 //protected int ixLineInit = 0; 1459 1460 //protected final float[][] values; 1461 1462 /**last x-positions for paint. 1463 * The lastPositionX[0] is used to repaint the last curve peace while shifting draw content. 1464 * Where, xShift (number of shifted pixel) is considered by subtraction. 1465 */ 1466 protected final int[] lastPositionX = new int[2]; 1467 1468 /**Number of values to show in graphic. */ 1469 protected int XXXnrofValuesShow; 1470 1471 /**The cary over of time which is not used for the current point. 1472 * 1473 */ 1474 protected int timeCaryOverNewValue; 1475 1476 /**Index of the last drawn values. 1477 * The index refers to the last drawn value. */ 1478 protected int ixDataDraw = 0; 1479 1480 /**The actual number of values which are not shown because its time difference is too small 1481 * to show in graphic as new point. It is counted only for debugging. 1482 */ 1483 protected int nrofValuesLessViewPart; 1484 1485 /**Pixel from right for the cursor1 and cursor2. If -1 then the cursor is unused yet. 1486 */ 1487 protected int xpCursor1 = -1; 1488 1489 /**Pixel from right for the cursor1 and cursor2. If -1 then the cursor is unused yet. 1490 */ 1491 protected int xpCursor2 = -1; 1492 1493 /**New Pixel position for a cursor1 and cursor2. If >=0 then a new position is given. 1494 */ 1495 protected int xpCursor1New = -1; 1496 1497 /**New Pixel position for a cursor1 and cursor2. If >=0 then a new position is given. 1498 */ 1499 protected int xpCursor2New = -1; 1500 1501 /**During mouse move, internal use. */ 1502 protected boolean bMouseDownCursor1; 1503 1504 /**During mouse move, internal use. */ 1505 protected boolean bMouseDownCursor2; 1506 1507 protected final static int cmdSetCursor = -2; 1508 1509 /**Position of cursor in the data. */ 1510 protected int ixDataCursor1; 1511 1512 /**Position of cursor in the data. */ 1513 protected int ixDataCursor2; 1514 1515 /**Distance of nrofValues for one vertical grid line (strong or not strong). */ 1516 protected int gridDistanceY; 1517 1518 /**Distance of percent of y-view for one vertical grid line (strong or not strong). */ 1519 protected float gridDistanceX; 1520 1521 /**period of strong lines. */ 1522 protected int gridStrongPeriodX; 1523 1524 /**period of strong lines. */ 1525 protected int gridStrongPeriodY; 1526 1527 protected GralColor gridColorGral; // = new Color(getDisplay(), 0, 255, 255); 1528 1529 protected GralColor gridColorGralStrong; // = new Color(getDisplay(), 0, 255, 255); 1530 1531 /**Zoom factor. If zoom = 1.0, the curve will be shown in the current canvas area. 1532 * It the zoom-factor is greater 1.0, not all points will be shown. 1533 * The origin point of the values is given with {@link #xOrigin} and {@link #yOrigin}. 1534 */ 1535 //protected float xZoom = 1.0F, yZoom= 1.0F; 1536 1537 /**Origin point from 0.0 to 1.0 for the zoomed area. */ 1538 //protected float xOrigin = 0.0F, yOrigin = 0.0F; 1539 1540 protected GralColor colorBackGral; // = new Color(getDisplay(), 0xff, 0xff, 0xff); 1541 1542 protected boolean focusChanged = false; //it doesn't work 1543 1544 /**last point in x where values were drawn. */ 1545 protected float xViewLastF = 0; 1546 1547 /**Number of iData-indices, which are shifted in the {@link #values}. 1548 * This number have to be shifted in the pixel area if a draw-all is not requested. 1549 * This field is set to 0 if the draw action is done.*/ 1550 protected AtomicInteger nrofDataShift = new AtomicInteger(0); 1551 1552 protected float nrofDataShiftFracPart = 0.0F; 1553 1554 /**Set true if {@link #redrawData()} is called. Then only the area for new data is drawn 1555 * in the {@link #drawBackground(GC, int, int, int, int)}-routine. 1556 * 1557 */ 1558 protected boolean redrawBecauseNewData; 1559 1560 protected boolean bRedrawAll; 1561 1562 private int ctLastSelected; 1563 1564 /**Set to true to force a paint all. */ 1565 protected boolean bPaintAllCmd = false; 1566 1567 protected GraphicImplAccess(GralCurveView outer){ 1568 super(outer); 1569 widgg = outer; 1570 this.pixelOrg.xPixelCurve = 0; 1571 this.pixelOrg.yPixelCurve = 0; 1572 1573 } 1574 1575 /**Forces that the next repaint paints the whole graphic. 1576 * This method should be invoked if the conditions for the graphic are changed, for example 1577 * changed colors for lines. 1578 */ 1579 protected void setPaintAllCmd(){ 1580 System.out.println("GralCurveView.Info - paintallTrigger;"); 1581 bPaintAllCmd = true; //used in implementation level to force a paint of the whole curves. 1582 widgg.bNewGetVariables= true; //used to get faulty variables newly with an error message. 1583 } 1584 1585 /**Gets the index in data with given pixel position in the graphic. 1586 * The wrapping index in the data is contained in {@link #ixDataShown}. 1587 * @param ixw pixel position countered from right side. 1588 * @return 1589 */ 1590 protected int getIxDataFromPixelRight(int ixPixelFromRight){ 1591 int ixDataWrap = ixDataShown[ixPixelFromRight]; 1592 int ixData = (ixDataWrap >> widgg.shIxiData) & widgg.mIxiData; 1593 return ixData; 1594 } 1595 1596 /**Determines and switches a curve to select by mouse click. 1597 * The curve should be at least 10 pixel near the mouse position. 1598 * <br> 1599 * Algorithm: From mouse position the index in the data are calculated: {@link #getIxDataFromPixelRight(int)}. 1600 * Any track is checked: The value (float) is read, the scaling is used to calculate the y-Position. 1601 * Then the distance between mouse and track is calculated. 1602 * 1603 * @param xpos Mouse position on click in pixel 1604 * @param ypos 1605 * @param xsize size of the curve view widget. 1606 * @param ysize 1607 * @return true if a track is selected. false if the position is more as 10 pixels far of any track. 1608 */ 1609 protected boolean selectTrack(int xpos, int ypos, int xsize, int ysize){ 1610 int ixData = getIxDataFromPixelRight(xsize - xpos); //index countered from right to left 1611 int minFound = 10; //at least 10 pixel near found curve. 1612 int maxDiffLastSelected = 0; 1613 Track foundTrack = null; 1614 ctLastSelected +=1; 1615 List<Track> listTracks1 = widgg.listTracks; //Atomic access, iterate in local referenced list. 1616 for(Track track: listTracks1){ //NOTE: break inside. 1617 float val = track.values[ixData]; 1618 float yFactor = ysize / -10.0F / track.scale.yScale; //y-scaling 1619 float y0Pix = (1.0F - track.scale.y0Line/100.0F) * ysize; //y0-line 1620 1621 int yp = (int)((val - track.scale.yOffset) * yFactor + y0Pix); 1622 int diff = Math.abs(yp - ypos); 1623 int diffLastSelected = ctLastSelected - track.identLastSelect; 1624 if(diff < minFound && diffLastSelected > maxDiffLastSelected){ 1625 foundTrack = track; 1626 maxDiffLastSelected = diffLastSelected; 1627 //minFound = diff; 1628 } 1629 } 1630 if(foundTrack !=null){ 1631 foundTrack.identLastSelect = ctLastSelected; 1632 trackSelected = foundTrack; 1633 if(widgg.actionSelectTrack !=null){ 1634 widgg.actionSelectTrack.exec(0, widgg, trackSelected); 1635 } 1636 } 1637 return foundTrack !=null; 1638 } 1639 1640 protected void setCursors(int xPos){ 1641 //System.out.println("middle"); 1642 int xr = pixelOrg.xPixelCurve - xPos; //from right 1643 if(xpCursor1 < 0){ //the right cursor 1644 xpCursor1New = xr; 1645 bMouseDownCursor1 = true; 1646 bMouseDownCursor2 = false; 1647 } else if(xpCursor2 < 0){ 1648 xpCursor2New = xr; 1649 bMouseDownCursor2 = true; 1650 bMouseDownCursor1 = false; 1651 } else { //decide which cursor 1652 //use the new changed value if not processed in graphic yet or the current cursor. 1653 int x1 = xpCursor1New >=0 ? xpCursor1New: xpCursor1; 1654 int x2 = xpCursor2New >=0 ? xpCursor2New: xpCursor2; 1655 if(x1 < x2){ 1656 int xp = x1; //swap 1657 xpCursor1New = x2; 1658 xpCursor2New = xp; 1659 //System.out.printf("GralCurveView - setCursors - swap; xC1=%d; xC2=%d\n", x2, xp); 1660 } 1661 int xm = (x1 + x2) /2; 1662 if(xr > xm){ //more left 1663 xpCursor1New = xr; 1664 bMouseDownCursor1 = true; 1665 bMouseDownCursor2 = false; 1666 //System.out.printf("GralCurveView - setCursors; cursor1=%d\n", xr); 1667 } else { 1668 xpCursor2New = xr; 1669 bMouseDownCursor2 = true; 1670 bMouseDownCursor1 = false; 1671 //System.out.printf("GralCurveView - setCursors; cursor2=%d\n", xr); 1672 } 1673 } 1674 bRedrawAll = true; 1675 widgg.repaint(0,0); 1676 //repaint(50,100); 1677 if(widgg.actionMoveCursor !=null){ 1678 widgg.actionMoveCursor.exec(0, widgg); 1679 } 1680 } 1681 1682 protected void moveCursor(int xMousePixel){ 1683 int xr = pixelOrg.xPixelCurve - xMousePixel; //from right 1684 if(bMouseDownCursor1){ 1685 xpCursor1New = xr; //from right; 1686 //System.out.println("SwtCurveView.mouseMove - cursor1; xr=" + xr); 1687 widgg.repaint(50,100); 1688 } else if(bMouseDownCursor2){ 1689 xpCursor2New = xr; //from right; 1690 //System.out.println("SwtCurveView.mouseMove - cursor2; xr=" + xr); 1691 widgg.repaint(50,100); 1692 } else { 1693 //System.out.println("SwtCurveView.mouseMove x,y=" + e.x + ", " + e.y); 1694 1695 } 1696 if(widgg.actionMoveCursor !=null){ 1697 widgg.actionMoveCursor.exec(0, widgg); 1698 } 1699 } 1700 1701 /**Zooms the curve presentation with same index right with a lesser time spread. 1702 * If the curve presentation is running yet, a finer solution in the present is given. 1703 * Note that in this case the right index is the actual write index.*/ 1704 protected void zoomToPresent(){ 1705 if(xpCursor1 >=0){ 1706 ixDataCursor1 = ixDataShown[xpCursor1]; 1707 } 1708 if(xpCursor2 >=0){ 1709 ixDataCursor2 = ixDataShown[xpCursor2]; 1710 } 1711 xpCursor1New = xpCursor2New = cmdSetCursor; 1712 if(widgg.timeorg.timeSpread > 100) { widgg.timeorg.timeSpread /=2; } 1713 else { widgg.timeorg.timeSpread = 100; } 1714 bPaintAllCmd = true; 1715 widgg.repaint(100, 200); 1716 } 1717 1718 /**Zooms the curve presentation with same index right with a greater time spread. 1719 * If the curve presentation is running yet, a broader solution in the present is given. 1720 * Note that in this case the right index is the actual write index.*/ 1721 protected void zoomToPast(){ 1722 //zoom out 1723 int maxTimeSpread = widgg.timeorg.lastShortTimeDateInCurve - widgg.timeorg.firstShortTimeDateInCurve; 1724 if(xpCursor1 >=0){ 1725 ixDataCursor1 = ixDataShown[xpCursor1]; 1726 } 1727 if(xpCursor2 >=0){ 1728 ixDataCursor2 = ixDataShown[xpCursor2]; 1729 } 1730 xpCursor1New = xpCursor2New = cmdSetCursor; 1731 if(widgg.timeorg.timeSpread < 0x3fffffff) { widgg.timeorg.timeSpread *=2; } 1732 else { widgg.timeorg.timeSpread = 0x7fffffff; } 1733 bPaintAllCmd = true; 1734 widgg.repaint(100, 200); 1735 1736 } 1737 1738 /**Zooms between the given vertical cursors. 1739 * The time spread is calculated so that the cursors are places on the same data indices at 1740 * 1/10 and 9/10 of the presentation range. 1741 */ 1742 protected void zoomBetweenCursors(){ 1743 ixDataCursor1 = ixDataShown[xpCursor1]; 1744 ixDataCursor2 = ixDataShown[xpCursor2]; 1745 ixDataShowRight = ixDataCursor2 + (((ixDataCursor2 - ixDataCursor1) / 10) & widgg.mIxData); 1746 int ixiData1 = (ixDataShown[xpCursor1] >> widgg.shIxiData) & widgg.mIxiData; 1747 int ixiData2 = (ixDataShown[xpCursor2] >> widgg.shIxiData) & widgg.mIxiData; 1748 int time1 = widgg.timeValues[ixiData1]; 1749 int time2 = widgg.timeValues[ixiData2]; 1750 if((time2 - time1)>0){ 1751 widgg.timeorg.timeSpread = (time2 - time1) * 10/8; 1752 assert(widgg.timeorg.timeSpread >0); 1753 } else { 1754 widgg.stop(); 1755 } 1756 xpCursor1New = xpCursor2New = cmdSetCursor; 1757 widgg.repaint(100, 200); 1758 1759 } 1760 1761 /**Unzooms between the given vertical cursors. 1762 * The cursors will be set newly in graphic at the same data indices. 1763 * The middle value of both cursors will be at the same position. 1764 * The time spread is multiplied by the value 5. 1765 * If the right border of presentation will be in the future, the {@link #ixDataShowRight} 1766 * will be greater than {@link #ixDataWr}, then the presentation will be adjusted to the current 1767 * right {@link #ixDataWr} and the cursor positions are changed adequate. 1768 * 1769 */ 1770 protected void cursorUnzoom() 1771 { 1772 int xpCursorMid = (xpCursor1 + xpCursor2) /2; 1773 int ixDataMid = ixDataShown[xpCursorMid]; //from Pixel value of cursor to data index 1774 //int timeMid = timeValues[(ixDataMid >> shIxiData) & mIxiData]; 1775 //int timeRight = timeValues[(ixDataShowRight >> shIxiData) & mIxiData]; 1776 //int dtime = timeRight - timeMid; 1777 //Assert.check(dtime > 0); 1778 ixDataShowRight = ixDataMid + (ixDataShowRight - ixDataMid) * 5; //5 times longer 1779 if((ixDataShowRight - widgg.ixDataWr) >0){ 1780 ixDataShowRight = widgg.ixDataWr; //show full to right 1781 } 1782 //ixDataCursor1 = ixDataShown[xpCursor1]; //from Pixel value of cursor to data index 1783 //ixDataCursor2 = ixDataShown[xpCursor2]; 1784 //ixDataShowRight = ixDataCursor2 + (((ixDataCursor2 - ixDataCursor1))); 1785 if(widgg.timeorg.timeSpread < (0x7ffffff5/5)) { widgg.timeorg.timeSpread *=5; } 1786 else { widgg.timeorg.timeSpread = 0x7fffffff; } 1787 /* 1788 int ixiData2 = (ixDataCursor2 >> shIxiData) & mIxiData; 1789 int time1 = timeValues[(ixDataCursor1 >> shIxiData) & mIxiData]; 1790 int time2 = timeValues[ixiData2]; 1791 timeSpread = (time2 - time1) * 5; 1792 */ 1793 Assert.check(widgg.timeorg.timeSpread >0); 1794 xpCursor1New = xpCursor2New = cmdSetCursor; 1795 widgg.repaint(100, 200); 1796 } 1797 1798 /**Shifts the curve presentation to the present (actual values). 1799 * If the TODO 1800 */ 1801 protected void viewToPresentOrGoIrRefresh() 1802 { 1803 if(widgg.common.bFreeze){ 1804 //assume that the same time is used for actual shown data spread as need 1805 //for the future. 1806 1807 //int timeRight = timeValues[(ixDataShowRight >> shIxiData) & mIxiData]; 1808 //int timeRightNew = timeRight + timeorg.timeSpread * 7/8; 1809 1810 int ixdDataSpread = ixDataShowRight - ixDataShown[pixelOrg.xPixelCurve * 5/8]; 1811 if((ixDataShowRight - widgg.ixDataWr)<0 && (ixDataShowRight - widgg.ixDataWr + ixdDataSpread) >=0){ 1812 //right end reached. 1813 ixDataShowRight = widgg.ixDataWr; 1814 widgg.common.bFreeze = false; 1815 } else { 1816 ixDataShowRight += ixdDataSpread; 1817 } 1818 //ixDataShowRight += ixdDataSpread; 1819 //if((ixDataShowRight - ixDataWr) > 0 && (ixDataShowRight - ixDataWr) < ixdDataSpread * 2) { 1820 //right end reached. 1821 //ixDataShowRight = ixDataWr; 1822 //common.bFreeze = false; 1823 //ixDataShowRight1 = ixDataWr + ixdDataSpread; 1824 //} 1825 //ixDataShowRight += ixDataShown[0] - ixDataShown[nrofValuesShow-1]; 1826 widgg.repaint(100, 200); 1827 1828 } else { 1829 setPaintAllCmd(); //refresh 1830 } 1831 //System.out.println("right-bottom"); 1832 } 1833 1834 /**Shifts the curve presentation to the past. If the presentation is running, it is stopped firstly. 1835 * If it is stopped, the data index on 20% of current presentation will be adjusted to the right border. 1836 * It means 20% are presented overlapping. The time spread is not changed. 1837 */ 1838 protected void stopAndViewToPast() 1839 { 1840 if(!widgg.common.bFreeze){ 1841 widgg.common.bFreeze = true; 1842 //now ixDataShow remain unchanged. 1843 } else { 1844 int ixdDataSpread = ixDataShowRight - ixDataShown[pixelOrg.xPixelCurve * 5/10]; 1845 //int ixDataShowRight1 = ixDataShown[nrofValuesShow * 7/8]; 1846 //int ixdDataSpread = ixDataShowRight - ixDataShowRight1; 1847 ixDataShowRight -= ixdDataSpread; 1848 if((ixDataShowRight - widgg.ixDataWr) < 0 && (widgg.ixDataWr - ixDataShowRight) < ixdDataSpread) { 1849 //left end reached. 1850 ixDataShowRight = widgg.ixDataWr + ixdDataSpread; 1851 } 1852 } 1853 widgg.repaint(100, 200); 1854 //System.out.println("left-bottom"); 1855 1856 } 1857 1858 protected void mouseSelectCursur(int xMousePixel, int yMousePixel){ 1859 1860 } 1861 1862 /**prepares indices of data. 1863 * All data have a timestamp. the xpixel-values are calculated from the timestamp. 1864 * The {@link #ixDataShown} array will be filled with the indices to the data for each x pixel from right to left. 1865 * If there are more as one data record for one pixel, the step width of ixData is >1, 1866 * If there are more as one x-pixel for one data record, the same index is written for that pixel 1867 * <br><br> 1868 * It uses the {@link timeValues} to get the timestamp of the values starting from param ixDataRight 1869 * for the right pixel of view. The ixDataRight will be decremented. It is a wrapping index with 1870 * step width of {@link #adIxData}. If the timestamp of any next index is greater than the last one, 1871 * it is a indication that any newer data are reached after wrapping. Then the preparation stops. 1872 * <br><br> 1873 * It fills {@link #ixDataShown} with indices to data per x pixel point. 1874 * If there are the same data for more as one pixel (zoomed near), then {@link #ixDataShown} will contain 1875 * the same index. The presentation can decide whether it should be shown with the same level (stepwise) 1876 * or with any linear approximation 1877 * 1878 * @param ixDataRight Index in data array for the right point. 1879 * It is a index which wraps around full integer range, see {@link #adIxData}. 1880 * @param xViewPart width of the spread to prepare in pixel 1881 * @return nrof pixel to draw. It is xViewPart if enough data are available, elsewhere less. 1882 * TODO move to ImplAccess 1883 */ 1884 protected int prepareIndicesDataForDrawing(int ixDataRight, int xViewPart, int timePart, boolean bPaintAll){ 1885 System.arraycopy(ixDataShown, 0, ixDataShown, xViewPart, ixDataShown.length - xViewPart); 1886 if(bPaintAll){ 1887 widgg.dataOrg.zPixelDataShown = 0; 1888 } else if(widgg.dataOrg.zPixelDataShown + xViewPart >= pixelOrg.xPixelCurve){ 1889 widgg.dataOrg.zPixelDataShown = pixelOrg.xPixelCurve; // the whole curve fills. 1890 } else { 1891 widgg.dataOrg.zPixelDataShown += xViewPart; 1892 } 1893 // 1894 // time, time2; ......timeRight the time stamps in data 1895 // ixData, ixData2, ..... ixDataRight the 32-bit-index to data 1896 // ixD the really index to data 1897 // 1898 ixDataShown[0] = ixDataRight; 1899 //System.out.println("GralCurveView - prepareIndices; timePart=" + timePart + "; timexPart=" + xViewPart * timePerPixel + "; xPart=" + xViewPart); 1900 int ixData = ixDataRight; 1901 int ixData2 = ixDataRight; 1902 int ixD = (ixData >> widgg.shIxiData) & widgg.mIxiData; 1903 int ixp2 = 0; 1904 int ixp = 0; //pixel from right to left 1905 int nrofPixel4Data =0; 1906 final int timeRight = widgg.timeValues[ixD]; //timestamp of the right value. 1907 // 1908 if(xViewPart > 100){ 1909 widgg.timeorg.timeLeftShowing = timeRight - (int)((xViewPart +1) * widgg.timeorg.timePerPixel); 1910 } 1911 //calculate absolute time from shorttime: 1912 long millisecAbs = widgg.timeorg.absTime.absTimeshort(timeRight); 1913 int milliSec2Div = (int)(millisecAbs % widgg.timeorg.millisecPerDiv); //how many millisec to the next division 1914 int milliSec2FineDiv = milliSec2Div % ( widgg.timeorg.millisecPerFineDiv); 1915 float pixel2FineDiv = milliSec2FineDiv * widgg.timeorg.pixel7time / widgg.timeorg.absTime.millisec7short(); //how many pixel to the next fine division line 1916 float pixel2Div = milliSec2Div * widgg.timeorg.pixel7time / widgg.timeorg.absTime.millisec7short(); //how many pixel to the next division line 1917 int ixPixelTimeDiv =-1; 1918 int ixPixelTimeDivFine =-1; //sets widgg.timeorg.sTimeAbsDiv[...], widgg.timeorg.xPixelTimeDivFine[...] TODO clean arrays before, better for debugging or set to -1 for stop point, see affter while-loop 1919 while(pixel2FineDiv < xViewPart ){ //&& nrofPixel4Data >=0){ 1920 if(Math.abs(pixel2Div - pixel2FineDiv) < 3){ 1921 //strong division 1922 int xPixel = (int)(pixel2Div + 1.5); // (xTimeDiv * widgg.timeorg.pixel7time + 0.5f) ; 1923 widgg.timeorg.xPixelTimeDiv[++ixPixelTimeDiv] = xPixel; 1924 widgg.timeorg.timeAbsOnLastStrongDiv = millisecAbs - milliSec2Div; //(long)(timeRight - xTimeDiv - widgg.timeorg.absTime_short + widgg.timeorg.absTime); 1925 if( widgg.timeorg.millisecPerFineDiv <= 100 ){ //less 1 sec for strong division: 1926 float millisec = (( widgg.timeorg.timeAbsOnLastStrongDiv) % 60000) / 1000.0f; 1927 widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = String.format("% 2.3f", millisec); 1928 1929 } else if( widgg.timeorg.millisecPerFineDiv < 1000){ //less 10 sec for strong division, but more 1 sec 1930 long seconds = (( widgg.timeorg.timeAbsOnLastStrongDiv / 1000) % 60); 1931 //System.out.println("time " + milliSec2Div + ", " + timeRight + ", " + widgg.timeorg.timeAbsOnLastStrongDiv); 1932 widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = "" + seconds; 1933 } else if( widgg.timeorg.millisecPerFineDiv < 10000){ //less 10 sec for strong division, but more 1 sec 1934 SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 1935 String sTime = format.format(new Date( widgg.timeorg.timeAbsOnLastStrongDiv)); 1936 widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = sTime; 1937 } else { 1938 SimpleDateFormat format = new SimpleDateFormat("HH:mm"); 1939 String sTime = format.format(new Date( widgg.timeorg.timeAbsOnLastStrongDiv)); 1940 widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = sTime; 1941 } 1942 widgg.timeorg.pixelWrittenAfterStrongDiv = xPixel; 1943 pixel2Div += widgg.timeorg.pixelPerTimeDiv; 1944 milliSec2Div += widgg.timeorg.millisecPerDiv; 1945 } else { 1946 int xPixel = (int)(pixel2FineDiv + 1.5f); //(int)(xTimeDivFine * widgg.timeorg.pixel7time + 0.5f) ; 1947 widgg.timeorg.xPixelTimeDivFine[++ixPixelTimeDivFine] = xPixel; 1948 //System.out.println("" + xPixel); 1949 } 1950 pixel2FineDiv += widgg.timeorg.pixelPerTimeFineDiv; 1951 //xTimeDivFine += widgg.timeorg.shortTimePerDiv; 1952 } 1953 widgg.timeorg.timeLeftShowing = timeRight; //for next call. 1954 // 1955 widgg.timeorg.xPixelTimeDiv[++ixPixelTimeDiv] = -1; //stopPoint; 1956 widgg.timeorg.xPixelTimeDivFine[++ixPixelTimeDivFine] = -1; //stopPoint; 1957 int time2 = timeRight; //start time 1958 int time = timeRight; 1959 int dtime2 = 0; 1960 //int dtime = 0; 1961 //xViewPart = nrof pixel from right 1962 //ixp1 counts from 0... right to left 1963 //int nrofValues1 = nrofValues; 1964 while(ixp < xViewPart //Fills ixDataShown with the index to the data for each pixel. 1965 && dtime2 <=0 1966 && nrofPixel4Data >=0 //130328 1967 ){ // && ixp1 >= ixp2){ //singularly: ixp1 < ixp2 if a faulty timestamp is found. 1968 do{ //all values per 1 pixel 1969 ixData -= widgg.adIxData; //decrement to older values in the data /// 1970 if(ixData == - widgg.adIxData && !widgg.dataOrg.bWrappedInBuffer){ 1971 //wrapped but data are not wrapped: 1972 dtime2 = 1; //same like time crack 1973 } 1974 else { 1975 ixD = (ixData >> widgg.shIxiData) & widgg.mIxiData; //the correct index in data. 1976 time = widgg.timeValues[ixD]; //timestamp of that data point 1977 dtime2 = time - time2; //difference time from the last one. It is negative. 1978 //dtime = time9 - time; //offset to first right point 1979 if((dtime2) <0){ //from rigth to left, dtime2 <0 is expected 1980 int dtime0 = timeRight - time; //time difference to the right pointt 1981 int ixp3 = (int)(dtime0 * widgg.timeorg.pixel7time + 0.5f); //calculate pixel offset from right, right =0 1982 if(ixp3 > xViewPart){ //next time stamp is in the past of requested view 1983 ixp3 = xViewPart; //no more than requested nr of points. 1984 } 1985 nrofPixel4Data += (ixp3 - ixp); 1986 if(ixp3 < ixp) 1987 Debugutil.stop(); 1988 ixp = ixp3; 1989 if(xpCursor1New == cmdSetCursor && ixData == ixDataCursor1){ 1990 xpCursor1New = ixp; //set cursor xp if the data index is gotten. 1991 } 1992 if(xpCursor2New == cmdSetCursor && ixData == ixDataCursor2){ 1993 xpCursor2New = ixp; 1994 } 1995 } 1996 } 1997 } while( ixp == ixp2 //all values for the same 1 pixel 1998 && dtime2 <0 //stop at time crack to newer values. 1999 //&& nrofValues >0 2000 //&& ixData != ixDataRight //no more as one wrap around, if dtime == 0 or is less 2001 ); 2002 // 2003 //if(ixp1 > ixp2 && ixp1 <= size_x){ //prevent drawing on false timestamp in data (missing data) 2004 while(ixp2 < ixp) { 2005 ixDataShown[ixp2] = ixData2; //same index to more as one point. 2006 this.nrofPixel4data[ixp2] = --nrofPixel4Data; 2007 ixp2 +=1; 2008 } 2009 if(widgg.dataOrg.zPixelDataShown < ixp2){ 2010 widgg.dataOrg.zPixelDataShown = ixp2; //max. value of shown data. 2011 } 2012 ixData2 = ixData; 2013 } 2014 if(ixp < xViewPart){ 2015 //System.out.println("GralCurveView large xViewPart"); 2016 } 2017 //ixDataShown[++ixp2] = -1; //the last 2018 return ixp; 2019 } 2020 2021 } 2022 2023 2024 2025 2026 2027 2028 2029 protected class GralCurveViewMouseAction implements GralMouseWidgetAction_ifc { 2030 2031 @Override public void mouse1Down(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg) 2032 { 2033 if(xMousePixel < xWidgetSizePixel / 20 && yMousePixel > yWidgetSizePixel * 95/100){ 2034 //left side bottom 2035 System.out.println("GralCurveView-Mousedown left bottom"); 2036 ((GraphicImplAccess)_wdgImpl).stopAndViewToPast(); 2037 } else if(xMousePixel > xWidgetSizePixel * 95 / 100 && yMousePixel > yWidgetSizePixel * 95/100){ 2038 //right side bottom 2039 //System.out.println("GralCurveView-Mousedown rigth bottom"); 2040 ((GraphicImplAccess)_wdgImpl).viewToPresentOrGoIrRefresh(); 2041 } else if(xMousePixel < xWidgetSizePixel / 20 && yMousePixel < yWidgetSizePixel / 20){ 2042 //left side top 2043 System.out.println("GralCurveView-Mousedown left top"); 2044 ((GraphicImplAccess)_wdgImpl).zoomToPast(); 2045 } else if(xMousePixel > xWidgetSizePixel * 95 / 100 && yMousePixel < yWidgetSizePixel / 20){ 2046 //right side top 2047 //System.out.println("GralCurveView-Mousedown rigth top"); 2048 ((GraphicImplAccess)_wdgImpl).zoomToPresent(); 2049 } else { 2050 if(!((GraphicImplAccess)_wdgImpl).selectTrack(xMousePixel, yMousePixel, xWidgetSizePixel, yWidgetSizePixel)){ 2051 ((GraphicImplAccess)_wdgImpl).setCursors(xMousePixel); 2052 } 2053 //if((key & KeyCode.mAddKeys) ==KeyCode.ctrl){ 2054 //} 2055 } 2056 } 2057 2058 @Override public void mouse1Up(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg) 2059 { 2060 ((GraphicImplAccess)_wdgImpl).bMouseDownCursor1 = ((GraphicImplAccess)_wdgImpl).bMouseDownCursor2 = false; 2061 //System.out.println("GralCurveView - MouseUp; "); 2062 } 2063 2064 @Override 2065 public void mouse2Down(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, 2066 GralWidget widgg) 2067 { 2068 // TODO Auto-generated method stub 2069 2070 } 2071 2072 @Override 2073 public void mouse2Up(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, 2074 GralWidget widgg) 2075 { 2076 // TODO Auto-generated method stub 2077 2078 } 2079 2080 @Override 2081 public void mouse1Double(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg) 2082 { 2083 } 2084 2085 @Override public boolean mouseMoved(int xMousePixel, int yMousePixel, int xWidgetPixelSize, int yWidgetPixelSize) 2086 { 2087 ((GraphicImplAccess)_wdgImpl).moveCursor(xMousePixel); 2088 return true; 2089 } 2090 2091 2092 } 2093 2094 public GralUserAction actionZoomBetweenCursors = new GralUserAction("actionZoomBetweenCursors"){ 2095 @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){ 2096 if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){ 2097 ((GraphicImplAccess)_wdgImpl).zoomBetweenCursors(); 2098 } 2099 return true; 2100 } 2101 }; 2102 2103 public GralUserAction actionZoomOut = new GralUserAction("actionZoomOut"){ 2104 @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){ 2105 if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){ 2106 ((GraphicImplAccess)_wdgImpl).cursorUnzoom(); 2107 } 2108 return true; 2109 } 2110 }; 2111 2112 2113 public GralUserAction actionCleanBuffer = new GralUserAction("actionCleanBuffer"){ 2114 @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){ 2115 if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){ 2116 cleanBuffer();; 2117 } 2118 return true; 2119 } 2120 }; 2121 2122 2123 public GralUserAction actionGo = new GralUserAction("actionGo"){ 2124 @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){ 2125 if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){ 2126 ((GraphicImplAccess)_wdgImpl).ixDataShowRight = ixDataWr; 2127 common.bFreeze = false; 2128 } 2129 return true; 2130 } 2131 }; 2132 2133 public GralUserAction actionPaintAll = new GralUserAction("actionPaintAll"){ 2134 @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){ 2135 if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){ 2136 ((GraphicImplAccess)_wdgImpl).setPaintAllCmd(); 2137 } 2138 return true; 2139 } 2140 }; 2141 2142}