├── .gitignore ├── .idea ├── .name ├── PiMonitor.iml ├── encodings.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml ├── vcs.xml └── workspace.xml ├── .settings ├── org.eclipse.core.resources.prefs └── org.eclipse.ltk.core.refactoring.prefs ├── README.md ├── data ├── data.pkl ├── logger.dtd ├── logger_METRIC_EN_v131.xml ├── logger_METRIC_EN_v263.xml └── logger_STD_EN_DS2_v352.xml ├── pimonitor ├── PM.py ├── PMConnection.py ├── PMDemoConnection.py ├── PMMain.py ├── PMPacket.py ├── PMParameter.py ├── PMUtils.py ├── PMXmlParser.py ├── __init__.py ├── cu │ ├── PMCUAddress.py │ ├── PMCUCalculatedParameter.py │ ├── PMCUContext.py │ ├── PMCUConversion.py │ ├── PMCUFixedAddressParameter.py │ ├── PMCUParameter.py │ ├── PMCUStandardParameter.py │ ├── PMCUSwitchParameter.py │ └── __init__.py ├── test │ ├── PMCUTest.py │ └── __init__.py └── ui │ ├── PMScreen.py │ ├── PMSingleWindow.py │ ├── PMWindow.py │ └── __init__.py ├── res └── subaru_logo.png └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | /venv/ 3 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | PiMonitor -------------------------------------------------------------------------------- /.idea/PiMonitor.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 67 | 68 | 69 | 73 | 74 | 75 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 97 | 98 | 99 | 100 | 103 | 104 | 107 | 108 | 109 | 110 | 113 | 114 | 117 | 118 | 121 | 122 | 123 | 124 | 127 | 128 | 131 | 132 | 135 | 136 | 137 | 138 | 141 | 142 | 145 | 146 | 149 | 150 | 153 | 154 | 155 | 156 | 159 | 160 | 163 | 164 | 167 | 168 | 171 | 172 | 173 | 174 | 177 | 178 | 181 | 182 | 185 | 186 | 189 | 190 | 191 | 192 | 195 | 196 | 199 | 200 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 259 | 260 | 261 | 283 | 284 | 285 | 309 | 310 | 311 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 1406746534774 368 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 421 | 422 | 431 | 432 | 433 | 436 | 439 | 440 | 441 | 442 | 443 | 444 | file://$PROJECT_DIR$/pimonitor/ui/PMWindow.py 445 | 58 446 | 448 | 449 | file://$PROJECT_DIR$/pimonitor/cu/PMCUCalculatedParameter.py 450 | 31 451 | 453 | 454 | file://$PROJECT_DIR$/pimonitor/ui/PMWindow.py 455 | 94 456 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//pimonitor/PMMain.py=utf-8 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.ltk.core.refactoring.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PiMonitor 2 | ========= 3 | 4 | Subaru Select Monitor (SSM) reader 5 | 6 | https://www.youtube.com/watch?v=M2AWh8OudeA 7 | 8 | https://www.youtube.com/watch?v=jiXd1-ZefJg 9 | -------------------------------------------------------------------------------- /data/data.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elewarr/PiMonitor/8e425c3ca725355faa8ad0648c2b8de9473ef8ac/data/data.pkl -------------------------------------------------------------------------------- /data/logger.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /pimonitor/PM.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 22-04-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | 8 | class PM(object): 9 | _instance = None 10 | 11 | def __new__(cls, *args, **kwargs): 12 | if not cls._instance: 13 | cls._instance = super(PM, cls).__new__(cls, *args) 14 | return cls._instance 15 | 16 | def __init__(self): 17 | pass 18 | 19 | def set(self, log): 20 | self._log = log 21 | 22 | @classmethod 23 | def log(cls, message, mid=0): 24 | return PM().log_impl(message, mid) 25 | 26 | def log_impl(self, message, mid): 27 | if self._log is not None: 28 | return self._log(message, mid) 29 | else: 30 | return -1 31 | -------------------------------------------------------------------------------- /pimonitor/PMConnection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 29-03-2013 3 | 4 | @author: citan 5 | """ 6 | import serial 7 | import time 8 | 9 | from pimonitor.PMPacket import PMPacket 10 | 11 | 12 | class PMConnection(object): 13 | """ 14 | classdocs 15 | """ 16 | 17 | def __init__(self): 18 | """ 19 | Constructor 20 | """ 21 | self._ser = None 22 | 23 | def open(self): 24 | self._ser = serial.Serial( 25 | port='/dev/ttyUSB0', 26 | # port='/dev/tty.usbserial-000013FA', 27 | baudrate=4800, 28 | timeout=2000, 29 | writeTimeout=55, 30 | parity=serial.PARITY_NONE, 31 | stopbits=serial.STOPBITS_ONE, 32 | bytesize=serial.EIGHTBITS) 33 | time.sleep(0.2) 34 | 35 | return self._ser is not None 36 | 37 | def close(self): 38 | if self._ser is not None: 39 | self._ser.close() 40 | 41 | def init(self, target): 42 | request_packet = PMPacket(self.get_destination(target), 0xF0, [0xBF]) 43 | return self.send_packet(request_packet) 44 | 45 | def send_packet(self, packet): 46 | self._ser.write(packet.to_string()) 47 | time.sleep(0.05) 48 | 49 | out_packet = None 50 | data = [] 51 | 52 | while self._ser.inWaiting() > 0: 53 | 54 | # read header 55 | tmp = self._ser.read(3) 56 | data.extend(tmp) 57 | 58 | # read size 59 | sizebytes = self._ser.read() 60 | data.append(sizebytes[0]) 61 | size = ord(sizebytes[0]) 62 | 63 | # read data 64 | tmp = self._ser.read(size) 65 | data.extend(tmp) 66 | 67 | # read checksum 68 | data.extend(self._ser.read()) 69 | data = map(ord, data) 70 | 71 | out_packet = PMPacket.from_array(data) 72 | data = [] 73 | 74 | if packet.is_equal(out_packet): 75 | continue 76 | 77 | return out_packet 78 | 79 | def read_parameter(self, parameter): 80 | address = parameter.get_address().get_address() 81 | address_len = parameter.get_address().get_length() 82 | 83 | data = [0xA8, 0x00] 84 | 85 | for i in range(0, address_len): 86 | target_address = address + i 87 | data.append((target_address & 0xffffff) >> 16) 88 | data.append((target_address & 0xffff) >> 8) 89 | data.append(target_address & 0xff) 90 | 91 | request_packet = PMPacket(self.get_destination(parameter.get_target()), 0xf0, data) 92 | return self.send_packet(request_packet) 93 | 94 | def read_parameters(self, parameters): 95 | data = [] 96 | target = parameters[0].get_target() 97 | 98 | data.append(0xA8) 99 | data.append(0x00) 100 | for parameter in parameters: 101 | # TODO: 102 | if target != parameter.get_target() and target & 0x01 != parameter.get_target() & 0x01 and target & 0x02 != parameter.get_target() & 0x02: 103 | raise Exception('connection', "targets differ: " + str(target) + " vs " + str(parameter.get_target())) 104 | 105 | address = parameter.get_address().get_address() 106 | address_len = parameter.get_address().get_length() 107 | for i in range(0, address_len): 108 | target_address = address + i 109 | data.append((target_address & 0xffffff) >> 16) 110 | data.append((target_address & 0xffff) >> 8) 111 | data.append(target_address & 0xff) 112 | 113 | request_packet = PMPacket(self.get_destination(target), 0xf0, data) 114 | out_packet = self.send_packet(request_packet) 115 | 116 | out_data = out_packet.get_data() 117 | out_packets = [] 118 | data_offset = 1 # skip E8 119 | 120 | for parameter in parameters: 121 | address_len = parameter.get_address().get_length() 122 | single_out_data = [0xE8] 123 | single_out_data.extend(out_data[data_offset:address_len + data_offset]) 124 | single_out_packet = PMPacket(out_packet.get_destination(), out_packet.get_source(), single_out_data) 125 | out_packets.append(single_out_packet) 126 | data_offset += address_len 127 | 128 | return out_packets 129 | 130 | @staticmethod 131 | def get_destination(target): 132 | dst = target 133 | if target == 1: 134 | dst = 0x10 135 | if target == 2: 136 | dst = 0x18 137 | if target == 3: 138 | dst = 0x10 139 | return dst 140 | -------------------------------------------------------------------------------- /pimonitor/PMDemoConnection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 29-03-2013 3 | 4 | @author: citan 5 | """ 6 | from pimonitor.PM import PM 7 | from pimonitor.PMPacket import PMPacket 8 | import random 9 | import time 10 | 11 | 12 | class PMDemoConnection(object): 13 | 14 | def __init__(self): 15 | self._ser = None 16 | self._log_id = None 17 | random.seed() 18 | 19 | self._byteval = [0, 0, 0, 0] 20 | 21 | def open(self): 22 | message = 'Opening serial connection...' 23 | self._log_id = PM.log(message) 24 | time.sleep(0.2) 25 | PM.log(message + " [DONE]", self._log_id) 26 | 27 | return True 28 | 29 | def close(self): 30 | PM.log("Closing serial connection", self._log_id) 31 | pass 32 | 33 | def init(self, target): 34 | PM.log('Initializing CU for target: ' + str(target), self._log_id) 35 | response = [] 36 | if target == 1 or target == 3: 37 | response = [0x80, 0xF0, 0x10, 0x69, 0xFF, 0xA2, 0x10, 0x02, 0x4D, 0x12, 0x04, 0x40, 0x06, 0xF3, 0xFA, 0xC9, 0x8E, 0x22, 0x04, 0x02, 0xAC, 0x00, 0x00, 0x00, 0x60, 0xCE, 0x54, 0xF8, 0xB9, 0x84, 0x00, 0x6C, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x45, 0x1F, 0x30, 0x80, 0xF0, 0x20, 0x1F, 0x02, 0x43, 0xFB, 0x00, 0xF1, 0xC1, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26] 38 | if target == 2: 39 | response = [0x80, 0xF0, 0x18, 0x39, 0xFF, 0xA2, 0x10, 0x21, 0xD0, 0xF3, 0x70, 0x31, 0x00, 0x01, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0xBD, 0xC3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x3E, 0x00, 0x0B, 0x21, 0xC0, 0x00, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51] 40 | 41 | return PMPacket.from_array(response) 42 | 43 | def read_parameter(self, parameter): 44 | time.sleep(0.05) 45 | address_len = parameter.get_address().get_length() 46 | 47 | data = [0x80, 0xF0] # [0x80, 0xF0, 0x18, 0x02, 0xE8, 0x60, 0xD2] 48 | 49 | if parameter.get_target() == 1 or parameter.get_target() == 3: 50 | data.append(0x10) 51 | if parameter.get_target() == 2: 52 | data.append(0x18) 53 | 54 | data.append(address_len+1) 55 | data.append(0xE8) 56 | 57 | for i in range(0, address_len): 58 | #self._byteval[i] = (self._byteval[i] + 1) % 0xFF 59 | self._byteval[i] = 100 % 0xFF 60 | data.append(self._byteval[i]) 61 | 62 | checksum = 0 63 | for b in data: 64 | checksum = (checksum + b) & 0xFF 65 | 66 | data.append(checksum) 67 | 68 | return PMPacket.from_array(data) 69 | 70 | def read_parameters(self, parameters): 71 | time.sleep(0.05) 72 | out_packets = [] 73 | 74 | for parameter in parameters: 75 | out_packets.append(self.read_parameter(parameter)) 76 | 77 | return out_packets 78 | -------------------------------------------------------------------------------- /pimonitor/PMMain.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Created on 29-03-2013 5 | 6 | @author: citan 7 | """ 8 | 9 | import os 10 | import os.path 11 | import time 12 | import cPickle as pickle 13 | import platform 14 | import re 15 | import sys 16 | 17 | from pimonitor.PM import PM 18 | from pimonitor.PMConnection import PMConnection 19 | from pimonitor.PMDemoConnection import PMDemoConnection 20 | from pimonitor.PMXmlParser import PMXmlParser 21 | from pimonitor.cu.PMCUParameter import PMCUParameter 22 | from pimonitor.cu.PMCUContext import PMCUContext 23 | from pimonitor.ui.PMScreen import PMScreen 24 | from pimonitor.ui.PMSingleWindow import PMSingleWindow 25 | from pimonitor.ui.PMWindow import PMWindow 26 | 27 | 28 | def stringSplitByNumbers(x): 29 | r = re.compile('(\d+)') 30 | l = r.split(x.get_id()) 31 | return [int(y) if y.isdigit() else y for y in l] 32 | 33 | 34 | if __name__ == '__main__': 35 | 36 | if platform.system() == "Linux": 37 | from evdev import InputDevice, list_devices 38 | 39 | devices = map(InputDevice, list_devices()) 40 | eventX = "" 41 | for dev in devices: 42 | if dev.name == "ADS7846 Touchscreen": 43 | eventX = dev.fn 44 | 45 | os.environ["SDL_FBDEV"] = "/dev/fb1" 46 | os.environ["SDL_MOUSEDRV"] = "TSLIB" 47 | os.environ["SDL_MOUSEDEV"] = eventX 48 | 49 | screen = PMScreen() 50 | log_id = PM.log('Application started') 51 | 52 | screen.render() 53 | 54 | parser = PMXmlParser() 55 | 56 | supported_parameters = [] 57 | 58 | if os.path.isfile("data/data.pkl"): 59 | serializedDataFile = open("data/data.pkl", "rb") 60 | defined_parameters = pickle.load(serializedDataFile) 61 | serializedDataFile.close() 62 | else: 63 | defined_parameters = parser.parse("logger_METRIC_EN_v352.xml") 64 | defined_parameters = sorted(defined_parameters, key=lambda x: x.get_id(), reverse=True) 65 | output = open("data/data.pkl", "wb") 66 | pickle.dump(defined_parameters, output, -1) 67 | output.close() 68 | 69 | if len(sys.argv) > 1 and sys.argv[1] == "demo": 70 | connection = PMDemoConnection() 71 | elif platform.system() == "Linux": 72 | connection = PMConnection() 73 | else: 74 | connection = PMDemoConnection() 75 | 76 | while True: 77 | try: 78 | connection.open() 79 | ecu_packet = connection.init(1) 80 | tcu_packet = connection.init(2) 81 | 82 | ecu_context = PMCUContext(ecu_packet, [1, 3]) 83 | ecu_parameters = ecu_context.match_parameters(defined_parameters) 84 | ecu_switch_parameters = ecu_context.match_switch_parameters(defined_parameters) 85 | ecu_calculated_parameters = ecu_context.match_calculated_parameters(defined_parameters, ecu_parameters) 86 | 87 | tcu_context = PMCUContext(tcu_packet, [2]) 88 | tcu_parameters = tcu_context.match_parameters(defined_parameters) 89 | tcu_switch_parameters = tcu_context.match_switch_parameters(defined_parameters) 90 | tcu_calculated_parameters = tcu_context.match_calculated_parameters(defined_parameters, tcu_parameters) 91 | 92 | PM.log("ECU ROM ID: " + ecu_context.get_rom_id()) 93 | PM.log("TCU ROM ID: " + tcu_context.get_rom_id()) 94 | 95 | supported_parameters = ecu_parameters + ecu_switch_parameters + ecu_calculated_parameters + tcu_parameters + tcu_switch_parameters + tcu_calculated_parameters 96 | 97 | supported_parameters = sorted(supported_parameters, key=stringSplitByNumbers) 98 | 99 | # pids = ["E114", "P104", "P122", "P97", "P203"] 100 | # first_window_parameters = [] 101 | # 102 | # for parameter in supported_parameters: 103 | # if parameter.get_id() in pids: 104 | # pids.remove(parameter.get_id()) 105 | # first_window_parameters.append(parameter) 106 | # 107 | # window = PMWindow(first_window_parameters) 108 | # screen.add_window(window) 109 | 110 | for parameter in supported_parameters: 111 | window = PMSingleWindow(parameter) 112 | screen.add_window(window) 113 | 114 | screen.next_window() 115 | 116 | while True: 117 | window = screen.get_window() 118 | parameters = window.get_parameters() 119 | 120 | # TODO refactor - not possible to test at the moment, so leave working part untouched 121 | if len(parameters) == 1: 122 | parameter = parameters[0] 123 | if parameter.get_cu_type() == PMCUParameter.CU_TYPE_STD_PARAMETER(): 124 | packet = connection.read_parameter(parameter) 125 | window.set_packets([packet]) 126 | elif parameter.get_cu_type() == PMCUParameter.CU_TYPE_FIXED_ADDRESS_PARAMETER(): 127 | packet = connection.read_parameter(parameter) 128 | window.set_packets([packet]) 129 | elif parameter.get_cu_type() == PMCUParameter.CU_TYPE_SWITCH_PARAMETER(): 130 | packet = connection.read_parameter(parameter) 131 | window.set_packets([packet]) 132 | elif parameter.get_cu_type() == PMCUParameter.CU_TYPE_CALCULATED_PARAMETER(): 133 | packets = connection.read_parameters(parameter.get_dependencies()) 134 | window.set_packets(packets) 135 | elif len(parameters) > 1: 136 | packets = connection.read_parameters(parameters) 137 | window.set_packets(packets) 138 | 139 | screen.render() 140 | 141 | except IOError as e: 142 | PM.log('I/O error: {0} {1}'.format(e.errno, e.strerror), log_id) 143 | if connection is not None: 144 | connection.close() 145 | time.sleep(3) 146 | continue 147 | 148 | screen.close() 149 | -------------------------------------------------------------------------------- /pimonitor/PMPacket.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 13-04-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import array 8 | 9 | 10 | class PMPacket(object): 11 | # 0x80 12 | # destination byte 13 | # source byte 14 | # data size byte 15 | # ... 16 | # checksum byte sum of every byte in packet (incl. header) 17 | 18 | _header_byte = 0x80 19 | _valid_bytes = [0xFF, 0xA8, 0xE8] 20 | 21 | def __init__(self, dst, src, data): 22 | self._dst = dst 23 | self._src = src 24 | self._data = data 25 | 26 | @classmethod 27 | def from_array(cls, data): 28 | validate = PMPacket.is_valid(data) 29 | if not validate[0]: 30 | raise Exception('packet', validate[1]) 31 | 32 | dst = data[1] 33 | src = data[2] 34 | data = data[4:-1] 35 | return cls(dst, src, data) 36 | 37 | @classmethod 38 | def is_valid(cls, data): 39 | # TODO: check E8 40 | valid = True 41 | msg = "" 42 | 43 | valid = valid and (len(data) > 5) 44 | msg += "invalid length (too short), " if (not valid) else "" 45 | 46 | valid = valid and (data[0] == PMPacket._header_byte) 47 | msg += "invalid header, " if (not valid) else "" 48 | 49 | # valid = data[4] in PMPacket._valid_bytes 50 | # msg += "invalid header, expected one of " + ', '.join(hex(s) for s in PMPacket._valid_bytes) +", got: " + hex(data[4]) + ", " if (not valid) else "" 51 | #valid = valid and ((data[1] == 0x10) or (data[1] == 0xf0)) 52 | #valid = valid and ((data[2] == 0x10) or (data[2] == 0xf0)) 53 | #valid = valid and (data[1] != data[2]) 54 | #msg += "invalid source/target, " if (not valid) else "" 55 | 56 | current_len = len(data) 57 | expected_len = 5 + data[3] 58 | valid = valid and (current_len == expected_len) 59 | msg += "invalid length (is: " + str(current_len) + ", expected: " + str(expected_len) + "), " if ( 60 | not valid) else "" 61 | 62 | checksum = 0 63 | for i in range(0, len(data) - 1): 64 | checksum = (checksum + data[i]) & 0xFF 65 | 66 | valid = valid and (checksum == data[-1]) 67 | msg += "invalid checksum (is " + str(checksum) + ", expected: " + str(data[-1]) + "), " if (not valid) else "" 68 | 69 | return valid, msg 70 | 71 | def is_equal(self, packet): 72 | return self.to_bytes() == packet.to_bytes() 73 | 74 | def to_bytes(self): 75 | length = len(self._data) 76 | 77 | packet = [self._header_byte, self._dst, self._src, length] 78 | packet.extend(self._data) 79 | 80 | checksum = 0 81 | for b in packet: 82 | checksum = (checksum + b) & 0xFF 83 | 84 | packet.append(checksum) 85 | return packet 86 | 87 | def to_string(self): 88 | return array.array('B', self.to_bytes()).tostring() 89 | 90 | def dump(self): 91 | return "[" + ', '.join(("0x%0.2X" % s) for s in self.to_bytes()) + "], dst: " + hex( 92 | self._dst) + ", src: " + hex(self._src) + ", len: " + str(len(self._data)) 93 | 94 | def get_data(self): 95 | return self._data 96 | 97 | def get_destination(self): 98 | return self._dst 99 | 100 | def get_source(self): 101 | return self._src 102 | 103 | @classmethod 104 | def dump_header(cls, data): 105 | print("header [" + ', '.join(hex(s) for s in data) + "], len: " + str(len(data))) 106 | -------------------------------------------------------------------------------- /pimonitor/PMParameter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 29-03-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import re 8 | 9 | from pimonitor.PMPacket import PMPacket 10 | 11 | 12 | class PMParameter(object): 13 | """ 14 | classdocs 15 | """ 16 | 17 | def __init__(self, pid, name, desc, byte_index, bit_index, target): 18 | """ 19 | Constructor 20 | """ 21 | self._id = pid 22 | self._name = name 23 | self._desc = desc 24 | self._byte_index = byte_index 25 | self._bit_index = bit_index 26 | self._target = target 27 | self._conversions = [] 28 | self._dependencies = [] 29 | self._parameters = [] 30 | self._address = 0 31 | self._address_length = 0 32 | self._ecu_ids = {} 33 | 34 | def get_id(self): 35 | return self._id 36 | 37 | def set_address(self, address, length): 38 | self._address = address 39 | self._address_length = length 40 | 41 | def get_address(self): 42 | return self._address 43 | 44 | def get_address_length(self): 45 | return self._address_length 46 | 47 | def get_target(self): 48 | return self._target 49 | 50 | def get_name(self): 51 | return self._name 52 | 53 | def add_conversion(self, conversion): 54 | self._conversions.append(conversion) 55 | 56 | def add_dependency(self, dependency): 57 | self._dependencies.append(dependency) 58 | 59 | def get_dependencies(self): 60 | return self._dependencies 61 | 62 | def add_parameter(self, parameter): 63 | self._parameters.append(parameter) 64 | 65 | def get_parameters(self): 66 | return self._parameters 67 | 68 | def init_ecu_id(self, ecu_id): 69 | self._ecu_ids[ecu_id] = [] 70 | 71 | def get_ecu_id(self, ecu_id): 72 | return self._ecu_ids[ecu_id] 73 | 74 | def get_calculated_value(self, packets, unit=None): 75 | value = "" 76 | local_vars = locals() 77 | 78 | if len(self._conversions) > 0 and unit is None: 79 | unit = self._conversions[0][0] 80 | 81 | for conversion in self._conversions: 82 | currunit = conversion[0] 83 | expr = conversion[1] 84 | value_format = conversion[2] 85 | conversion_map = {} 86 | if unit == currunit: 87 | param_pairs = re.findall(r'\[([^]]*)\]', expr) 88 | for pair in param_pairs: 89 | attributes = pair.split(":") 90 | key = attributes[0] 91 | unit = attributes[1] 92 | expr = expr.replace("[" + key + ":" + unit + "]", key) 93 | conversion_map.update({key: unit}) 94 | 95 | param_no = 0 96 | for packet in packets: 97 | param = self._parameters[param_no] 98 | if param.get_id() in conversion_map: 99 | conversion_unit = conversion_map[param.get_id()] 100 | else: 101 | conversion_unit = None 102 | 103 | if param.get_dependencies(): 104 | return "DEPS :(" 105 | else: 106 | value = param.get_value(packet, conversion_unit) 107 | local_vars[param.get_id()] = float(value) 108 | param_no += 1 109 | 110 | try: 111 | value = eval(expr) 112 | except (SyntaxError, ZeroDivisionError): 113 | value = 0.0 114 | 115 | format_tokens = value_format.split(".") 116 | output_format = "%.0f" 117 | if len(format_tokens) > 1: 118 | output_format = "%." + str(len(format_tokens[1])) + "f" 119 | 120 | value = output_format % value 121 | 122 | return value 123 | 124 | # noinspection PyUnusedLocal 125 | def get_value(self, packet, unit=None): 126 | value = "" 127 | 128 | if len(self._conversions) > 0 and unit is None: 129 | unit = self._conversions[0][0] 130 | 131 | for conversion in self._conversions: 132 | currunit = conversion[0] 133 | expr = conversion[1] 134 | value_format = conversion[2] 135 | if unit == currunit: 136 | # ignore 0xe8 137 | index = 1 138 | x = 0 139 | value_bytes = packet.get_data()[index:index + self._address_length] 140 | if self._address_length == 1: 141 | x = value_bytes[0] 142 | 143 | if self._address_length == 2: 144 | x = (value_bytes[0] << 8) | value_bytes[1] 145 | 146 | if self._address_length == 4: 147 | x = (value_bytes[0] << 24) | (value_bytes[1] << 16) | (value_bytes[2] << 8) | value_bytes[2] 148 | 149 | try: 150 | value = eval(expr) 151 | except (SyntaxError, ZeroDivisionError): 152 | value = 0.0 153 | 154 | format_tokens = value_format.split(".") 155 | output_format = "%.0f" 156 | if len(format_tokens) > 1: 157 | output_format = "%." + str(len(format_tokens[1])) + "f" 158 | 159 | value = output_format % value 160 | 161 | return value 162 | 163 | def get_default_unit(self): 164 | if len(self._conversions) > 0: 165 | return self._conversions[0][0] 166 | return "" 167 | 168 | def switch_to_ecu_id(self, ecu_id): 169 | if ecu_id in self._ecu_ids: 170 | ecu_id_data = self._ecu_ids[ecu_id] 171 | self._address = ecu_id_data[0] 172 | self._address_length = ecu_id_data[1] 173 | 174 | def is_supported(self, data): 175 | if self._byte_index != "none" and self._bit_index != "none" and len(data) - 1 > self._byte_index: 176 | cubyte = data[1 + self._byte_index] 177 | bitmask = 1 << self._bit_index 178 | return cubyte & bitmask == bitmask 179 | elif self._ecu_ids is not None: 180 | return PMPacket.extract_rom_id(data) in self._ecu_ids 181 | else: 182 | return False 183 | 184 | def to_string(self): 185 | return "Param: id=" + self._id + ", name=" + self._name + ", desc=" + self._desc + ", byte=" + str( 186 | self._byte_index) + \ 187 | ", bit=" + str(self._bit_index) + ", target=" + str( 188 | self._target) + ", conversions=" + '[%s]' % ', '.join(map(str, self._conversions)) + \ 189 | ", address=" + hex(self._address) + "[" + str(self._address_length) + "]" 190 | -------------------------------------------------------------------------------- /pimonitor/PMUtils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 13-04-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import os 8 | from pimonitor.PM import PM 9 | 10 | 11 | class PMUtils(object): 12 | """ 13 | classdocs 14 | """ 15 | 16 | # Return CPU temperature as a character string 17 | @classmethod 18 | def get_cpu_temperature(cls): 19 | res = os.popen('vcgencmd measure_temp').readline() 20 | return res.replace("temp=", "").replace("'C\n", "") 21 | 22 | # Return RAM information (unit=kb) in a list 23 | # Index 0: total RAM 24 | # Index 1: used RAM 25 | # Index 2: free RAM 26 | @classmethod 27 | def get_ram_info(cls): 28 | p = os.popen('free') 29 | i = 0 30 | while 1: 31 | i += 1 32 | line = p.readline() 33 | if i == 2: 34 | return line.split()[1:4] 35 | 36 | # Return % of CPU used by user as a character string 37 | @classmethod 38 | def get_cpu_use(cls): 39 | return str(os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip()) 40 | 41 | # Return information about disk space as a list (unit included) 42 | # Index 0: total disk space 43 | # Index 1: used disk space 44 | # Index 2: remaining disk space 45 | # Index 3: percentage of disk used 46 | @classmethod 47 | def get_disk_space(cls): 48 | p = os.popen("df -h /") 49 | i = 0 50 | while 1: 51 | i += 1 52 | line = p.readline() 53 | if i == 2: 54 | return line.split()[1:5] 55 | 56 | @classmethod 57 | def log_os_stats(cls): 58 | try: 59 | cpu_temp = PMUtils.get_cpu_temperature() 60 | if len(cpu_temp) > 0: 61 | PM.log("CPU temp: " + cpu_temp) 62 | 63 | ram_stats = PMUtils.get_ram_info() 64 | if len(ram_stats) == 3: 65 | ram_free = round(int(ram_stats[2]) / 1000, 1) 66 | PM.log("RAM free: " + str(ram_free)) 67 | 68 | cpu_usage = PMUtils.get_cpu_use() 69 | if len(cpu_usage) > 0: 70 | PM.log("CPU usage: " + str(cpu_usage)) 71 | 72 | except IOError: 73 | pass -------------------------------------------------------------------------------- /pimonitor/PMXmlParser.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 29-03-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import xml.sax 8 | import os.path 9 | 10 | from pimonitor.PM import PM 11 | from pimonitor.cu.PMCUAddress import PMCUAddress 12 | from pimonitor.cu.PMCUCalculatedParameter import PMCUCalculatedParameter 13 | from pimonitor.cu.PMCUConversion import PMCUConversion 14 | from pimonitor.cu.PMCUFixedAddressParameter import PMCUFixedAddressParameter 15 | from pimonitor.cu.PMCUParameter import PMCUParameter 16 | from pimonitor.cu.PMCUStandardParameter import PMCUStandardParameter 17 | from pimonitor.cu.PMCUSwitchParameter import PMCUSwitchParameter 18 | 19 | # 20 | #
0x000007
21 | # 22 | # 23 | # 24 | #
25 | 26 | # 27 | # 28 | #
0x2186A
29 | #
30 | # ... ecu and conversions 31 | 32 | # 33 | # 34 | # 35 | # 36 | # 37 | # 38 | # 39 | # 40 | # 41 | 42 | # 43 | 44 | 45 | class PMXmlParser(xml.sax.ContentHandler): 46 | def __init__(self): 47 | xml.sax.ContentHandler.__init__(self) 48 | self._message = '' 49 | self._log_id = 0 50 | 51 | self._element_no = 0 52 | 53 | self._contexts = None 54 | 55 | self._parameter = None 56 | self._parameters = set() 57 | 58 | self._characters = '' 59 | self._ecu_ids = None 60 | self._address_length = 0 61 | self._proto_id = '' 62 | 63 | def parse(self, file_name): 64 | self._message = 'Parsing XML data' 65 | self._log_id = PM.log(self._message) 66 | 67 | file_path = os.path.join("data", file_name) 68 | source = open(file_path) 69 | xml.sax.parse(source, self) 70 | self.log_progress() 71 | PM.log(self._message + " [DONE]") 72 | 73 | return self._parameters 74 | 75 | def startElement(self, name, attrs): 76 | 77 | pid = None 78 | desc = None 79 | target = 0 80 | units = None 81 | expr = None 82 | value_format = None 83 | address = None 84 | 85 | byte_index = PMCUParameter.CU_INVALID_BYTE_INDEX() 86 | bit_index = PMCUParameter.CU_INVALID_BIT_INDEX() 87 | 88 | if name == "protocol": 89 | 90 | for (k, v) in attrs.items(): 91 | if k == "id": 92 | print 'protocol ' + v 93 | self._proto_id = v 94 | 95 | if self._proto_id != "SSM": 96 | return 97 | 98 | if name == "parameter": 99 | 100 | for (k, v) in attrs.items(): 101 | if k == "id": 102 | pid = v 103 | if k == "name": 104 | name = v 105 | if k == "desc": 106 | desc = v 107 | if k == "ecubyteindex": 108 | byte_index = int(v) 109 | if k == "ecubit": 110 | bit_index = int(v) 111 | if k == "target": 112 | target = int(v) 113 | 114 | if byte_index is not PMCUParameter.CU_INVALID_BYTE_INDEX() and bit_index is not PMCUParameter.CU_INVALID_BIT_INDEX(): 115 | self._parameter = PMCUStandardParameter(pid, name, desc, byte_index, bit_index, target) 116 | elif byte_index is PMCUParameter.CU_INVALID_BYTE_INDEX() and bit_index is PMCUParameter.CU_INVALID_BIT_INDEX(): 117 | self._parameter = PMCUCalculatedParameter(pid, name, desc, target) 118 | else: 119 | raise Exception 120 | 121 | elif name == "ecuparam": 122 | for (k, v) in attrs.items(): 123 | if k == "id": 124 | pid = v 125 | if k == "name": 126 | name = v 127 | if k == "desc": 128 | desc = v 129 | if k == "target": 130 | target = int(v) 131 | 132 | self._parameter = PMCUFixedAddressParameter(pid, name, desc, target) 133 | 134 | elif name == "switch": 135 | for (k, v) in attrs.items(): 136 | if k == "id": 137 | pid = v 138 | if k == "name": 139 | name = v 140 | if k == "desc": 141 | desc = v 142 | if k == "byte": 143 | address = int(v, 16) 144 | if k == "ecubyteindex": 145 | byte_index = int(v) 146 | if k == "bit": 147 | bit_index = int(v) 148 | if k == "target": 149 | target = int(v) 150 | 151 | self._parameter = PMCUSwitchParameter(pid, name, desc, address, byte_index, bit_index, target) 152 | 153 | elif name == "address": 154 | self._address_length = 1 155 | for (k, v) in attrs.items(): 156 | if k == "length": 157 | self._address_length = int(v) 158 | 159 | elif name == "conversion": 160 | for (k, v) in attrs.items(): 161 | if k == "units": 162 | units = v 163 | if k == "expr": 164 | expr = v 165 | if k == "format": 166 | value_format = v 167 | 168 | self._parameter.add_conversion(PMCUConversion(units, expr, value_format)) 169 | 170 | elif name == "ecu": 171 | for (k, v) in attrs.items(): 172 | if k == "id": 173 | self._ecu_ids = v.split(",") 174 | 175 | elif name == "ref": 176 | for (k, v) in attrs.items(): 177 | if k == "parameter": 178 | self._parameter.add_dependency(v) 179 | 180 | def characters(self, content): 181 | if self._proto_id != "SSM": 182 | return 183 | self._characters = self._characters + content 184 | 185 | def endElement(self, name): 186 | if self._proto_id != "SSM": 187 | return 188 | 189 | if name == "parameter": 190 | self._parameters.add(self._parameter) 191 | self._parameter = None 192 | 193 | elif name == "ecuparam": 194 | self._parameters.add(self._parameter) 195 | self._parameter = None 196 | 197 | elif name == "switch": 198 | self._parameters.add(self._parameter) 199 | self._parameter = None 200 | 201 | elif name == "address": 202 | self._characters = self._characters.strip() 203 | 204 | if len(self._characters.strip()) > 0: 205 | 206 | if self._parameter.get_cu_type() == PMCUParameter.CU_TYPE_STD_PARAMETER(): 207 | self._parameter.set_address(PMCUAddress(int(self._characters, 16), self._address_length)) 208 | elif self._parameter.get_cu_type() == PMCUParameter.CU_TYPE_FIXED_ADDRESS_PARAMETER(): 209 | address = PMCUAddress(int(self._characters, 16), self._address_length) 210 | for ecu_id in self._ecu_ids: 211 | self._parameter.add_ecu_id(ecu_id, address) 212 | 213 | self._address_length = 0 214 | self._characters = '' 215 | 216 | elif name == "ecu": 217 | self._ecu_ids = None 218 | 219 | self._element_no += 1 220 | 221 | if self._element_no % 1000 == 0: 222 | self.log_progress() 223 | 224 | def log_progress(self): 225 | PM.log(self._message + " " + str(self._element_no) + " elements, " + str(len(self._parameters)) + " parameters", 226 | self._log_id) 227 | -------------------------------------------------------------------------------- /pimonitor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elewarr/PiMonitor/8e425c3ca725355faa8ad0648c2b8de9473ef8ac/pimonitor/__init__.py -------------------------------------------------------------------------------- /pimonitor/cu/PMCUAddress.py: -------------------------------------------------------------------------------- 1 | __author__ = 'citan' 2 | 3 | class PMCUAddress(object): 4 | 5 | def __init__(self, address, length): 6 | self._address = address 7 | self._length = length 8 | 9 | def get_address(self): 10 | return self._address 11 | 12 | def get_length(self): 13 | return self._length 14 | 15 | def to_string(self): 16 | return "address=" + hex(self._address) + ", length=" + str(self._length) -------------------------------------------------------------------------------- /pimonitor/cu/PMCUCalculatedParameter.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from pimonitor.cu.PMCUParameter import PMCUParameter 4 | from pimonitor.cu.PMCUStandardParameter import PMCUStandardParameter 5 | 6 | __author__ = 'citan' 7 | 8 | class PMCUCalculatedParameter(PMCUStandardParameter): 9 | def __init__(self, pid, name, desc, target): 10 | PMCUStandardParameter.__init__(self, pid, name, desc, PMCUParameter.CU_INVALID_BYTE_INDEX(), 11 | PMCUParameter.CU_INVALID_BIT_INDEX(), target) 12 | 13 | self._cu_type = PMCUParameter.CU_TYPE_CALCULATED_PARAMETER() 14 | self._dependencies = [] 15 | 16 | def add_dependency(self, parameter): 17 | self._dependencies.append(parameter) 18 | 19 | def fill_dependencies(self, supported_parameters): 20 | parameters = [] 21 | for dependency in self._dependencies: 22 | for parameter in supported_parameters: 23 | if parameter.get_id() == dependency: 24 | parameters.append(parameter) 25 | break 26 | 27 | self._dependencies = parameters 28 | 29 | def get_dependencies(self): 30 | return self._dependencies 31 | 32 | def get_calculated_value(self, packets, unit=None): 33 | value = "" 34 | local_vars = locals() 35 | 36 | if len(self._conversions) > 0 and unit is None: 37 | unit = self._conversions[0].get_unit() 38 | 39 | for conversion in self._conversions: 40 | curr_unit = conversion.get_unit() 41 | expr = conversion.get_expr() 42 | value_format = conversion.get_format() 43 | 44 | conversion_map = {} 45 | 46 | if unit == curr_unit: 47 | param_pairs = re.findall(r'\[([^]]*)\]', expr) 48 | for pair in param_pairs: 49 | attributes = pair.split(":") 50 | key = attributes[0] 51 | unit = attributes[1] 52 | expr = expr.replace("[" + key + ":" + unit + "]", key) 53 | conversion_map.update({key: unit}) 54 | 55 | param_no = 0 56 | for param in self._dependencies: 57 | if param.get_id() in conversion_map: 58 | conversion_unit = conversion_map[param.get_id()] 59 | else: 60 | conversion_unit = None 61 | 62 | if param.get_cu_type() == PMCUParameter.CU_TYPE_CALCULATED_PARAMETER(): 63 | return "ERROR DEPS" #param.get_calculated_value(packets, conversion_unit) 64 | else: 65 | value = param.get_value(packets[param_no], conversion_unit) 66 | local_vars[param.get_id()] = float(value) 67 | param_no += 1 68 | 69 | try: 70 | value = eval(expr) 71 | except (SyntaxError, NameError): 72 | return "ERROR EVAL" 73 | except ZeroDivisionError: 74 | return "0.0" 75 | 76 | format_tokens = value_format.split(".") 77 | output_format = "%.0f" 78 | if len(format_tokens) > 1: 79 | output_format = "%." + str(len(format_tokens[1])) + "f" 80 | 81 | value = output_format % value 82 | 83 | return value 84 | 85 | def is_supported(self, parameters): 86 | param_ids = [p.get_id() for p in parameters] 87 | 88 | for dependency in self._dependencies: 89 | if dependency not in param_ids: 90 | return False 91 | 92 | return True 93 | 94 | def to_string(self): 95 | return "id=" + self._id + "\nname=" + self._name + "\ndesc=" + self._desc + "\ntarget=" + str( 96 | self._target) + "\nconversion:\n\t" + '%s' % ',\n\t'.join(x.to_string() for x in self._conversions) + \ 97 | '\ndependency: ' + '%s' % ', '.join(x for x in self._dependencies) 98 | -------------------------------------------------------------------------------- /pimonitor/cu/PMCUContext.py: -------------------------------------------------------------------------------- 1 | from pimonitor.cu.PMCUParameter import PMCUParameter 2 | 3 | __author__ = 'citan' 4 | 5 | 6 | class PMCUContext(object): 7 | @classmethod 8 | def RESPONSE_MARK_OFFSET(cls): 9 | return 4 10 | 11 | @classmethod 12 | def RESPONSE_ROM_ID_OFFSET(cls): 13 | return 8 14 | 15 | @classmethod 16 | def INITIAL_RESPONSE_MIN_LEN(cls): 17 | return 13 18 | 19 | def __init__(self, packet, targets): 20 | self._packet = packet 21 | self._targets = targets 22 | 23 | def get_rom_id(self): 24 | data = self._packet.to_bytes() 25 | 26 | if data[PMCUContext.RESPONSE_MARK_OFFSET()] != 0xFF: 27 | raise Exception('packet', "not valid init response: " + hex(data[0]) + " instead 0xFF") 28 | if len(data) < PMCUContext.INITIAL_RESPONSE_MIN_LEN(): 29 | raise Exception('packet', "not valid init response") 30 | 31 | rom_id = ((data[PMCUContext.RESPONSE_ROM_ID_OFFSET()] << 32) | 32 | (data[PMCUContext.RESPONSE_ROM_ID_OFFSET() + 1] << 24) | 33 | (data[PMCUContext.RESPONSE_ROM_ID_OFFSET() + 2] << 16) | 34 | (data[PMCUContext.RESPONSE_ROM_ID_OFFSET() + 3] << 8) | 35 | (data[PMCUContext.RESPONSE_ROM_ID_OFFSET() + 4])) & 0xFFFFFFFFFF 36 | 37 | rom_id = hex(rom_id).lstrip("0x").upper() 38 | if rom_id[-1] == "L": 39 | rom_id = rom_id[:-1] 40 | return rom_id 41 | 42 | def match_parameters(self, parameters): 43 | matched = [] 44 | rom_id = self.get_rom_id() 45 | print 'rom id=' + rom_id 46 | 47 | for parameter in parameters: 48 | if parameter.get_target() not in self._targets: 49 | continue 50 | 51 | cu_type = parameter.get_cu_type() 52 | 53 | if cu_type == PMCUParameter.CU_TYPE_STD_PARAMETER(): 54 | if parameter.is_supported(self._packet.to_bytes()): 55 | matched.append(parameter) 56 | elif cu_type == PMCUParameter.CU_TYPE_FIXED_ADDRESS_PARAMETER(): 57 | if parameter.is_supported(rom_id): 58 | print 'match=' + parameter.get_id() 59 | parameter.switch_to_id(rom_id) 60 | matched.append(parameter) 61 | 62 | return matched 63 | 64 | def match_switch_parameters(self, parameters): 65 | matched = [] 66 | 67 | for parameter in parameters: 68 | if parameter.get_target() not in self._targets: 69 | continue 70 | 71 | cu_type = parameter.get_cu_type() 72 | 73 | if cu_type == PMCUParameter.CU_TYPE_SWITCH_PARAMETER(): 74 | if parameter.is_supported(self._packet.to_bytes()): 75 | matched.append(parameter) 76 | 77 | return matched 78 | 79 | def match_calculated_parameters(self, parameters, supported_parameters): 80 | matched = [] 81 | 82 | for parameter in parameters: 83 | if parameter.get_target() not in self._targets: 84 | continue 85 | 86 | cu_type = parameter.get_cu_type() 87 | if cu_type == PMCUParameter.CU_TYPE_CALCULATED_PARAMETER(): 88 | if parameter.is_supported(supported_parameters): 89 | parameter.fill_dependencies(supported_parameters) 90 | matched.append(parameter) 91 | 92 | return matched 93 | -------------------------------------------------------------------------------- /pimonitor/cu/PMCUConversion.py: -------------------------------------------------------------------------------- 1 | __author__ = 'citan' 2 | 3 | class PMCUConversion(object): 4 | 5 | def __init__(self, unit, expr, format): 6 | self._unit = unit 7 | self._expr = expr 8 | self._format = format 9 | 10 | def get_unit(self): 11 | return self._unit 12 | 13 | def get_expr(self): 14 | return self._expr 15 | 16 | def get_format(self): 17 | return self._format 18 | 19 | def to_string(self): 20 | return "unit=" + self._unit + ", expr=" + self._expr + ", format=" + self._format -------------------------------------------------------------------------------- /pimonitor/cu/PMCUFixedAddressParameter.py: -------------------------------------------------------------------------------- 1 | from pimonitor.cu.PMCUParameter import PMCUParameter 2 | from pimonitor.cu.PMCUStandardParameter import PMCUStandardParameter 3 | 4 | __author__ = 'citan' 5 | 6 | class PMCUFixedAddressParameter(PMCUStandardParameter): 7 | def __init__(self, pid, name, desc, target): 8 | PMCUStandardParameter.__init__(self, pid, name, desc, PMCUParameter.CU_INVALID_BYTE_INDEX(), 9 | PMCUParameter.CU_INVALID_BIT_INDEX(), target) 10 | 11 | self._cu_type = PMCUParameter.CU_TYPE_FIXED_ADDRESS_PARAMETER() 12 | self._cu_ids = {} 13 | 14 | def add_ecu_id(self, cu_id, address): 15 | self._cu_ids[cu_id] = address 16 | 17 | def get_address_for_id(self, cu_id): 18 | if cu_id in self._cu_ids: 19 | return self._cu_ids[cu_id] 20 | else: 21 | return None 22 | 23 | def switch_to_id(self, cu_id): 24 | self._address = self.get_address_for_id(cu_id) 25 | 26 | def set_address(self, address): 27 | raise Exception 28 | 29 | def is_supported(self, cu_id): 30 | return self.get_address_for_id(cu_id) is not None 31 | 32 | def to_string(self): 33 | return "id=" + self._id + "\nname=" + self._name + "\ndesc=" + self._desc + "\ntarget=" + str( 34 | self._target) + "\nconversion:\n\t" + '%s' % '\n\t'.join(x.to_string() for x in self._conversions) + \ 35 | '\necu: ' + ' '.join(['\n\tid={}, {}'.format(k,v.to_string()) for k,v in self._cu_ids.iteritems()]) -------------------------------------------------------------------------------- /pimonitor/cu/PMCUParameter.py: -------------------------------------------------------------------------------- 1 | __author__ = 'citan' 2 | 3 | # 4 | #
0x000007
5 | # 6 | # 7 | # 8 | #
9 | 10 | class PMCUParameter(object): 11 | 12 | def __init__(self, cu_type): 13 | self._cu_type = cu_type 14 | 15 | def get_cu_type(self): 16 | return self._cu_type 17 | 18 | @classmethod 19 | def CU_TYPE_STD_PARAMETER(cls): 20 | return 0 21 | 22 | @classmethod 23 | def CU_TYPE_FIXED_ADDRESS_PARAMETER(cls): 24 | return 1 25 | 26 | @classmethod 27 | def CU_TYPE_SWITCH_PARAMETER(cls): 28 | return 2 29 | 30 | @classmethod 31 | def CU_TYPE_CALCULATED_PARAMETER(cls): 32 | return 3 33 | 34 | @classmethod 35 | def CU_INVALID_BYTE_INDEX(cls): 36 | return -1 37 | 38 | @classmethod 39 | def CU_INVALID_BIT_INDEX(cls): 40 | return -1 41 | -------------------------------------------------------------------------------- /pimonitor/cu/PMCUStandardParameter.py: -------------------------------------------------------------------------------- 1 | from pimonitor.cu.PMCUContext import PMCUContext 2 | from pimonitor.cu.PMCUParameter import PMCUParameter 3 | 4 | __author__ = 'citan' 5 | 6 | 7 | class PMCUStandardParameter(PMCUParameter): 8 | def __init__(self, pid, name, desc, byte_index, bit_index, target): 9 | PMCUParameter.__init__(self, PMCUParameter.CU_TYPE_STD_PARAMETER()) 10 | 11 | self._id = pid 12 | self._name = name 13 | self._desc = desc 14 | self._byte_index = byte_index 15 | self._bit_index = bit_index 16 | self._target = target 17 | self._conversions = [] 18 | self._address = None 19 | 20 | def get_id(self): 21 | return self._id 22 | 23 | def set_address(self, address): 24 | self._address = address 25 | 26 | def get_address(self): 27 | return self._address 28 | 29 | def get_target(self): 30 | return self._target 31 | 32 | def get_name(self): 33 | return self._name 34 | 35 | def add_conversion(self, conversion): 36 | self._conversions.append(conversion) 37 | 38 | # noinspection PyUnusedLocal 39 | def get_value(self, packet, unit=None): 40 | value = "" 41 | 42 | if len(self._conversions) > 0 and unit is None: 43 | unit = self._conversions[0].get_unit() 44 | 45 | for conversion in self._conversions: 46 | curr_unit = conversion.get_unit() 47 | expr = conversion.get_expr() 48 | value_format = conversion.get_format() 49 | 50 | if unit == curr_unit: 51 | # print 'unit=' + unit + ', expr=' + expr + ', format=' + value_format 52 | # ignore 0xe8 53 | index = 1 54 | x = 0 55 | value_bytes = packet.get_data()[index:index + self.get_address().get_length()] 56 | 57 | address_length = self.get_address().get_length() 58 | if address_length == 1: 59 | x = value_bytes[0] 60 | elif address_length == 2: 61 | x = (value_bytes[0] << 8) | value_bytes[1] 62 | elif address_length == 3: 63 | x = (value_bytes[0] << 16) | (value_bytes[1] << 8) | value_bytes[2] 64 | elif address_length == 4: 65 | x = (value_bytes[0] << 24) | (value_bytes[1] << 16) | (value_bytes[2] << 8) | value_bytes[3] 66 | #print 'x=' + str(x) 67 | x = float(x) 68 | 69 | try: 70 | value = eval(expr) 71 | except (SyntaxError, NameError): 72 | return "ERROR EVAL" 73 | except (ZeroDivisionError): 74 | return "0.0" 75 | 76 | #print 'value=' + str(value) 77 | value = float(value) 78 | format_tokens = value_format.split(".") 79 | output_format = "%.0f" 80 | if len(format_tokens) > 1: 81 | output_format = "%." + str(len(format_tokens[1])) + "f" 82 | 83 | value = output_format % value 84 | #print 'result=' + value 85 | 86 | return value 87 | 88 | def get_default_unit(self): 89 | if len(self._conversions) > 0: 90 | return self._conversions[0].get_unit() 91 | return "" 92 | 93 | def is_supported(self, data): 94 | offset = PMCUContext.RESPONSE_MARK_OFFSET() + 1 + self._byte_index 95 | # <, not <= because last one is checksum 96 | if offset < len(data): 97 | cu_byte = data[offset] 98 | bit_mask = 1 << self._bit_index 99 | return cu_byte & bit_mask == bit_mask 100 | else: 101 | return False 102 | 103 | def to_string(self): 104 | return "id=" + self._id + "\nname=" + self._name + "\ndesc=" + self._desc + "\nbyte=" + str( 105 | self._byte_index) + "\n" + self._address.to_string() + "\nbit=" + str( 106 | self._bit_index) + "\ntarget=" + str( 107 | self._target) + "\nconversion:\n\t" + '%s' % ',\n\t'.join(x.to_string() for x in self._conversions) 108 | -------------------------------------------------------------------------------- /pimonitor/cu/PMCUSwitchParameter.py: -------------------------------------------------------------------------------- 1 | from pimonitor.cu.PMCUAddress import PMCUAddress 2 | from pimonitor.cu.PMCUParameter import PMCUParameter 3 | from pimonitor.cu.PMCUStandardParameter import PMCUStandardParameter 4 | 5 | __author__ = 'citan' 6 | 7 | 8 | class PMCUSwitchParameter(PMCUStandardParameter): 9 | def __init__(self, pid, name, desc, address, byte_index, bit_index, target): 10 | PMCUStandardParameter.__init__(self, pid, name, desc, byte_index, 11 | bit_index, target) 12 | 13 | self._cu_type = PMCUParameter.CU_TYPE_SWITCH_PARAMETER() 14 | self.set_address(PMCUAddress(address, 1)) 15 | 16 | def get_value(self, packet): 17 | index = 1 18 | value_byte = packet.get_data()[index:index + self._address.get_length()][0] 19 | 20 | bit_mask = 1 << self._bit_index 21 | if value_byte & bit_mask == bit_mask: 22 | return "1" 23 | else: 24 | return "0" 25 | 26 | def to_string(self): 27 | return "id=" + self._id + "\nname=" + self._name + "\ndesc=" + self._desc + "\nbyte=" + str( 28 | self._byte_index) + "\n" + self._address.to_string() + "\nbit=" + str( 29 | self._bit_index) + "\ntarget=" + str( 30 | self._target) 31 | -------------------------------------------------------------------------------- /pimonitor/cu/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'citan' 2 | -------------------------------------------------------------------------------- /pimonitor/test/PMCUTest.py: -------------------------------------------------------------------------------- 1 | from pimonitor.PMXmlParser import PMXmlParser 2 | from pimonitor.cu.PMCUContext import PMCUContext 3 | 4 | __author__ = 'citan' 5 | 6 | import unittest 7 | 8 | from pimonitor.PMDemoConnection import PMDemoConnection 9 | from pimonitor.PM import PM 10 | 11 | class PMCUTestCase(unittest.TestCase): 12 | 13 | def setUp(self): 14 | self._ecu_packet = None 15 | self._tcu_packet = None 16 | 17 | self._connection = None 18 | 19 | self._parameters = None 20 | 21 | self._ecu_context = None 22 | self._tcu_context = None 23 | 24 | self._ecu_parameters = None 25 | self._ecu_switch_parameters = None 26 | self._ecu_calculated_parameters = None 27 | 28 | self._tcu_parameters = None 29 | self._tcu_calculated_parameters = None 30 | self._tcu_switch_parameters = None 31 | 32 | logger = PM() 33 | logger.set(self.log) 34 | 35 | def prepare_1_open_connection(self): 36 | self._connection = PMDemoConnection() 37 | 38 | result = self._connection.open() 39 | self.assertTrue(result) 40 | 41 | def prepare_2_init_connection(self): 42 | self.prepare_1_open_connection() 43 | self.assertIsNotNone(self._connection) 44 | 45 | self._ecu_packet = self._connection.init(1) 46 | self.assertIsNotNone(self._ecu_packet) 47 | 48 | self._tcu_packet = self._connection.init(2) 49 | self.assertIsNotNone(self._tcu_packet) 50 | 51 | def prepare_3_parse_logger_definition(self): 52 | self.prepare_2_init_connection() 53 | parser = PMXmlParser() 54 | 55 | self._parameters = parser.parse("logger_METRIC_EN_v263.xml") 56 | 57 | self._parameters = sorted(self._parameters, key=lambda x: x.get_id(), reverse=True) 58 | 59 | self.assertIsNotNone(self._parameters) 60 | self.assertEqual(len(self._parameters), 716) 61 | 62 | def prepare_4_match_parameters(self): 63 | self.prepare_3_parse_logger_definition() 64 | 65 | self._ecu_context = PMCUContext(self._ecu_packet, [1, 3]) 66 | self._ecu_parameters = self._ecu_context.match_parameters(self._parameters) 67 | self.assertIsNotNone(self._ecu_parameters) 68 | self.assertEqual(len(self._ecu_parameters), 125) 69 | 70 | self._ecu_switch_parameters = self._ecu_context.match_switch_parameters(self._parameters) 71 | self.assertIsNotNone(self._ecu_switch_parameters) 72 | self.assertEqual(len(self._ecu_switch_parameters), 36) 73 | 74 | self._ecu_calculated_parameters = self._ecu_context.match_calculated_parameters(self._parameters, self._ecu_parameters) 75 | self.assertIsNotNone(self._ecu_calculated_parameters) 76 | self.assertEqual(len(self._ecu_calculated_parameters), 4) 77 | 78 | self._tcu_context = PMCUContext(self._tcu_packet, [2]) 79 | self._tcu_parameters = self._tcu_context.match_parameters(self._parameters) 80 | self.assertIsNotNone(self._tcu_parameters) 81 | self.assertEqual(len(self._tcu_parameters), 11) 82 | 83 | self._tcu_switch_parameters = self._tcu_context.match_switch_parameters(self._parameters) 84 | self.assertIsNotNone(self._tcu_switch_parameters) 85 | self.assertEqual(len(self._tcu_switch_parameters), 13) 86 | 87 | self._tcu_calculated_parameters = self._tcu_context.match_calculated_parameters(self._parameters, self._tcu_parameters) 88 | self.assertIsNotNone(self._tcu_calculated_parameters) 89 | self.assertEqual(len(self._tcu_calculated_parameters), 0) 90 | 91 | #TODO: switches 92 | 93 | def test_5_read_parameters(self): 94 | self.prepare_4_match_parameters() 95 | 96 | print self._ecu_parameters[120].to_string() 97 | packet = self._connection.read_parameter(self._ecu_parameters[120]) 98 | value = self._ecu_parameters[120].get_value(packet) 99 | print 'value=' + value 100 | 101 | def log(self, message, mid): 102 | print message 103 | 104 | return mid 105 | 106 | if __name__ == '__main__': 107 | unittest.main() 108 | -------------------------------------------------------------------------------- /pimonitor/test/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'citan' 2 | -------------------------------------------------------------------------------- /pimonitor/ui/PMScreen.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 18-04-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import os 8 | import sys 9 | import platform 10 | 11 | import pygame 12 | 13 | from pimonitor.PM import PM 14 | from pimonitor.PMUtils import PMUtils 15 | 16 | 17 | class PMScreen(object): 18 | """ 19 | classdocs 20 | """ 21 | LOG_FPS_EVENT = pygame.USEREVENT + 1 22 | LOG_STATS_EVENT = LOG_FPS_EVENT + 1 23 | ONE_SEC_EVENT = LOG_STATS_EVENT + 1 24 | 25 | def __init__(self): 26 | """ 27 | Constructor 28 | """ 29 | pygame.init() 30 | pygame.mouse.set_visible(False) 31 | 32 | # seems to suit RPi 33 | self._color_depth = 16 34 | 35 | if platform.system() == "Linux": 36 | pygame.display.set_mode((0, 0), pygame.FULLSCREEN, self._color_depth) 37 | else: 38 | pygame.display.set_mode((640, 480), 0, self._color_depth) 39 | 40 | self._surface = pygame.display.get_surface() 41 | 42 | self._clock = pygame.time.Clock() 43 | 44 | self._width = self._surface.get_width() 45 | self._height = self._surface.get_height() 46 | 47 | self._subwindow_alpha = 200 48 | 49 | self._font_size = int(self._height / 14) 50 | self._font = pygame.font.SysFont(pygame.font.get_default_font(), self._font_size) 51 | self._font_aa = 0 52 | self._fg_color = pygame.Color(255, 191, 0) 53 | self._bg_color = pygame.Color(0, 0, 0) 54 | self._dim_color = pygame.Color(200, 140, 0) 55 | 56 | self._log_lines = 4 57 | self._log_msg_id = 0 58 | self._log_surface = pygame.Surface((self._width / 2, self._font_size * self._log_lines), 0, self._color_depth) 59 | self._log_surface.set_alpha(self._subwindow_alpha) 60 | self._log_queue = [] 61 | 62 | logger = PM() 63 | logger.set(self.log) 64 | 65 | self._fps_log_id = 0 66 | self._frame_no = 0 67 | self.load_resources() 68 | pygame.time.set_timer(PMScreen.LOG_FPS_EVENT, 10000) 69 | pygame.time.set_timer(PMScreen.LOG_STATS_EVENT, 30000) 70 | pygame.time.set_timer(PMScreen.ONE_SEC_EVENT, 1000) 71 | 72 | self._window = None 73 | self._windows = [] 74 | 75 | self._pos_log_id = 0 76 | self._mouse_down_pos = (0, 0) 77 | self._mouse_down_mark_timeout = 0 78 | 79 | def clear(self): 80 | self._surface.fill(self._bg_color) 81 | 82 | def load_resources(self): 83 | self._bg_img = pygame.image.load(os.path.join('res', 'subaru_logo.png')).convert() 84 | self._bg_img_rect = self._bg_img.get_rect() 85 | 86 | def render(self): 87 | self._clock.tick() 88 | 89 | for event in pygame.event.get(): 90 | if event.type == PMScreen.LOG_FPS_EVENT: 91 | self._frame_no += 1 92 | self._fps_log_id = PM.log("FPS %.2f" % self._clock.get_fps(), self._fps_log_id) 93 | 94 | elif event.type == PMScreen.LOG_STATS_EVENT: 95 | #if platform.system() == "Linux": 96 | # PMUtils.log_os_stats() 97 | pass 98 | elif event.type == pygame.QUIT: 99 | self.close() 100 | sys.exit() 101 | 102 | elif event.type == pygame.MOUSEBUTTONUP: 103 | self._mouse_down_mark_timeout = 0 104 | self._mouse_down_pos = pygame.mouse.get_pos() 105 | self._pos_log_id = PM.log('Mouse up at: %s/%s' % pygame.mouse.get_pos(), self._pos_log_id) 106 | if self._mouse_down_pos[0] < self._width / 2: 107 | self.prev_window() 108 | else: 109 | self.next_window() 110 | elif event.type == pygame.K_LEFT: 111 | self.prev_window() 112 | elif event.type == pygame.K_RIGHT: 113 | self.next_window() 114 | elif event.type == PMScreen.ONE_SEC_EVENT: 115 | self._mouse_down_mark_timeout += 1 116 | 117 | self.clear() 118 | 119 | if self._window is None: 120 | self.render_bg() 121 | 122 | if self._window is not None: 123 | self._window.render() 124 | 125 | self.render_log() 126 | 127 | if self._mouse_down_mark_timeout < 2: 128 | pygame.draw.circle(self._surface, self._dim_color, self._mouse_down_pos, 16) 129 | 130 | pygame.display.update() 131 | 132 | def render_log(self): 133 | self.purge_logs() 134 | if len(self._log_queue) == 0: 135 | return 136 | 137 | self._log_surface.fill(self._bg_color) 138 | 139 | log_pos = 0 140 | for msg_entry in self._log_queue: 141 | msg_entry[2] += 1 142 | message = msg_entry[1] 143 | message_lbl = self._font.render(message, self._font_aa, self._fg_color, self._bg_color) 144 | self._log_surface.blit(message_lbl, (0, log_pos)) 145 | log_pos += self._font_size 146 | 147 | self._surface.blit(self._log_surface, (0, self._height - self._log_surface.get_height())) 148 | 149 | def render_bg(self): 150 | # self._surface.blit(self._bg_img, self._bg_img_rect) 151 | pass 152 | 153 | def purge_oldest_log(self): 154 | to_be_deleted = None 155 | oldest = pygame.time.get_ticks() 156 | 157 | for log_entry in self._log_queue: 158 | if log_entry[2] < oldest: 159 | oldest = log_entry[2] 160 | to_be_deleted = log_entry 161 | 162 | if to_be_deleted is not None: 163 | self._log_queue.remove(to_be_deleted) 164 | 165 | def purge_logs(self): 166 | to_be_deleted = [] 167 | 168 | for log_entry in self._log_queue: 169 | if pygame.time.get_ticks() - log_entry[2] > 5000: 170 | to_be_deleted.append(log_entry) 171 | 172 | for log_entry in to_be_deleted: 173 | self._log_queue.remove(log_entry) 174 | 175 | def add_window(self, window): 176 | self._windows.append(window) 177 | 178 | def set_window(self, window): 179 | if self._window is not None: 180 | self._window.set_surface(None) 181 | 182 | self._window = window 183 | self._window.set_surface(self._surface) 184 | 185 | def get_window(self): 186 | return self._window 187 | 188 | def next_window(self): 189 | if not self._windows: 190 | return 191 | 192 | if self._window is not None: 193 | index = self._windows.index(self._window) 194 | else: 195 | index = -1 196 | 197 | new_index = (index + 1) % len(self._windows) 198 | self.set_window(self._windows[new_index]) 199 | self.log_window(new_index) 200 | 201 | def prev_window(self): 202 | if not self._windows: 203 | return 204 | 205 | if self._window is not None: 206 | index = self._windows.index(self._window) 207 | else: 208 | index = 1 209 | 210 | new_index = (index - 1) % len(self._windows) 211 | self.set_window(self._windows[new_index]) 212 | self.log_window(new_index) 213 | 214 | def log_window(self, index): 215 | if self._window is not None: 216 | PM.log(str(index + 1) + '/' + str(len(self._windows)) + ': ' + self._window.get_parameters()[0].get_id(), 0) 217 | 218 | def log(self, message, mid): 219 | ticks = pygame.time.get_ticks() 220 | if mid == 0: 221 | self._log_msg_id = (self._log_msg_id % 1000) + 1 222 | mid = self._log_msg_id 223 | 224 | found = False 225 | for log_entry in self._log_queue: 226 | if log_entry[0] == mid: 227 | log_entry[1] = message 228 | found = True 229 | log_entry[2] = ticks 230 | 231 | self.purge_logs() 232 | 233 | # remove old messages if necessary 234 | if not found: 235 | if len(self._log_queue) >= self._log_lines: 236 | self.purge_oldest_log() 237 | self._log_queue.append([mid, message, ticks]) 238 | 239 | self.render() 240 | 241 | return mid 242 | 243 | def close(self): 244 | pygame.display.quit() 245 | -------------------------------------------------------------------------------- /pimonitor/ui/PMSingleWindow.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 22-04-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import pygame 8 | from pimonitor.cu.PMCUParameter import PMCUParameter 9 | 10 | 11 | class PMSingleWindow(object): 12 | """ 13 | classdocs 14 | """ 15 | 16 | def __init__(self, parameter): 17 | self._fg_color = pygame.Color(230, 166, 0) 18 | self._fg_color_dim = pygame.Color(200, 140, 0) 19 | self._bg_color = pygame.Color(0, 0, 0) 20 | self._parameters = [parameter] 21 | self._packets = None 22 | 23 | self._x_offset = 0 24 | self._sum_value = 0.0 25 | self._readings = 0 26 | 27 | def set_surface(self, surface): 28 | if surface is None: 29 | return 30 | 31 | parameter = self._parameters[0] 32 | 33 | self._surface = surface 34 | self._width = self._surface.get_width() 35 | self._height = self._surface.get_height() 36 | 37 | self._title_font_size = int(self._surface.get_height() / 12) 38 | self._value_font_size = int(self._surface.get_height() / 1.8) 39 | self._unit_font_size = int(self._surface.get_height() / 4) 40 | 41 | self._title_font = pygame.font.SysFont(pygame.font.get_default_font(), self._title_font_size) 42 | self._value_font = pygame.font.SysFont(pygame.font.get_default_font(), self._value_font_size) 43 | self._unit_font = pygame.font.SysFont(pygame.font.get_default_font(), self._unit_font_size) 44 | 45 | self._font_aa = 1 46 | 47 | self._title_lbl = self._title_font.render(parameter.get_name(), self._font_aa, self._fg_color) 48 | 49 | self._unit_lbl = self._unit_font.render(parameter.get_default_unit(), self._font_aa, self._fg_color_dim) 50 | self._end_x_offset = self._width - self._unit_lbl.get_width() - 10 51 | 52 | def render(self): 53 | value = "??" 54 | parameter = self._parameters[0] 55 | if self._packets is not None: 56 | if parameter.get_cu_type() == PMCUParameter.CU_TYPE_CALCULATED_PARAMETER(): 57 | value = parameter.get_calculated_value(self._packets) 58 | else: 59 | value = parameter.get_value(self._packets[0]) 60 | 61 | try: 62 | self._sum_value += float(value) 63 | self._readings += 1 64 | except: 65 | pass 66 | 67 | value_lbl_width = self._value_font.render(value, self._font_aa, self._fg_color).get_width() 68 | self._x_offset = (self._width - value_lbl_width) / 2 69 | value_lbl = self._value_font.render(value, self._font_aa, self._fg_color) 70 | 71 | avg_value_lbl = None 72 | if self._readings != 0: 73 | avg_value_lbl = self._unit_font.render("%.2f" % (self._sum_value / self._readings), self._font_aa, self._fg_color_dim) 74 | self._surface.blit(avg_value_lbl, (self._x_offset + value_lbl_width/2, 10 + self._title_lbl.get_height() + value_lbl.get_height())) 75 | 76 | self._surface.blit(self._title_lbl, (2, 2)) 77 | self._surface.blit(value_lbl, (self._x_offset, 10 + self._title_font_size)) 78 | if avg_value_lbl == None: 79 | self._surface.blit(self._unit_lbl, (self._end_x_offset, 10 + self._title_lbl.get_height() + value_lbl.get_height())) 80 | else: 81 | self._surface.blit(self._unit_lbl, (self._end_x_offset, 10 + self._title_lbl.get_height() + value_lbl.get_height() + avg_value_lbl.get_height())) 82 | 83 | 84 | def set_packets(self, packets): 85 | self._packets = packets 86 | 87 | def get_parameters(self): 88 | return self._parameters 89 | -------------------------------------------------------------------------------- /pimonitor/ui/PMWindow.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created on 22-04-2013 3 | 4 | @author: citan 5 | """ 6 | 7 | import pygame 8 | from pimonitor.cu.PMCUParameter import PMCUParameter 9 | 10 | 11 | class PMWindow(object): 12 | """ 13 | classdocs 14 | """ 15 | 16 | def __init__(self, parameters): 17 | self._fg_color = pygame.Color(200, 140, 0) 18 | self._bg_color = pygame.Color(0, 0, 0) 19 | self._dict = dict() 20 | 21 | self._parameters = parameters 22 | self._packets = None 23 | 24 | self._reading_no = 0 25 | self._sum_fuel_consumption = 0.0 26 | 27 | def set_surface(self, surface): 28 | 29 | if surface is None: 30 | return 31 | 32 | self._surface = surface 33 | self._width = self._surface.get_width() 34 | self._height = self._surface.get_height() 35 | 36 | self._title_font_size = int(self._surface.get_height() / 16) 37 | self._value_font_size = int(self._surface.get_height() / 3.4) 38 | self._title_font = pygame.font.SysFont(pygame.font.get_default_font(), self._title_font_size) 39 | self._value_font = pygame.font.SysFont(pygame.font.get_default_font(), self._value_font_size) 40 | 41 | self._font_aa = 1 42 | 43 | self._value_lbl_width = self._value_font.render("999", self._font_aa, self._fg_color).get_width() 44 | 45 | def render(self): 46 | 47 | if self._packets is None: 48 | return 49 | 50 | 51 | first_row_height = self._title_font_size + self._value_font_size + 10 52 | second_row_height = first_row_height + self._title_font_size + self._value_font_size + 20 53 | pygame.draw.line(self._surface, self._fg_color, (0, first_row_height + 10), 54 | (self._width, first_row_height + 10)) 55 | 56 | parameter_number = 0 57 | for parameter in self._parameters: 58 | 59 | if parameter.get_cu_type() == PMCUParameter.CU_TYPE_CALCULATED_PARAMETER(): 60 | value = parameter.get_calculated_value(self._packets[len(self._parameters)-1:]) 61 | self._reading_no += 1 62 | self._sum_fuel_consumption += float(value) 63 | else: 64 | value = parameter.get_value(self._packets[parameter_number]) 65 | 66 | 67 | title = parameter.get_name() # + " (" + param.get_default_unit() + ")" 68 | 69 | first_row_ids = ["E114", "P104", "P122"] 70 | if parameter.get_id() in first_row_ids: 71 | index = first_row_ids.index(parameter.get_id()) 72 | x_offset = (self._width / len(first_row_ids)) * index + 2 73 | 74 | titlelbl = self._title_font.render(title, self._font_aa, self._fg_color) 75 | valuelbl = self._value_font.render(value, self._font_aa, self._fg_color) 76 | self._surface.blit(titlelbl, (x_offset + 4, 10)) 77 | self._surface.blit(valuelbl, (x_offset + 4, 10 + self._title_font_size)) 78 | 79 | pygame.draw.line(self._surface, self._fg_color, (x_offset, 0), (x_offset, first_row_height)) 80 | 81 | second_row_ids = ["P203"] 82 | 83 | if parameter.get_id() in second_row_ids: 84 | index = second_row_ids.index(parameter.get_id()) 85 | x_offset = (self._width / len(second_row_ids)) * index + 2 86 | 87 | titlelbl = self._title_font.render(title, self._font_aa, self._fg_color) 88 | valuelbl = self._value_font.render(value, self._font_aa, self._fg_color) 89 | self._surface.blit(titlelbl, (x_offset + 4, first_row_height + 20)) 90 | self._surface.blit(valuelbl, (x_offset + 4, first_row_height + 20 + self._title_font_size)) 91 | 92 | pygame.draw.line(self._surface, self._fg_color, (x_offset, first_row_height + 20), 93 | (x_offset, second_row_height)) 94 | 95 | index = 1 96 | x_offset = (self._width / 2) * index + 2 97 | 98 | titlelbl = self._title_font.render("Average Fuel Cons. l/100km", self._font_aa, self._fg_color) 99 | valuelbl = self._value_font.render(str(self._sum_fuel_consumption/self._reading_no), self._font_aa, self._fg_color) 100 | self._surface.blit(titlelbl, (x_offset + 4, first_row_height + 20)) 101 | self._surface.blit(valuelbl, (x_offset + 4, first_row_height + 20 + self._title_font_size)) 102 | 103 | pygame.draw.line(self._surface, self._fg_color, (x_offset, first_row_height + 20), 104 | (x_offset, second_row_height)) 105 | 106 | parameter_number += 1 107 | 108 | def get_parameters(self): 109 | return self._parameters[:-1] + self._parameters[-1].get_dependencies() 110 | 111 | def set_packets(self, packets): 112 | self._packets = packets 113 | -------------------------------------------------------------------------------- /pimonitor/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elewarr/PiMonitor/8e425c3ca725355faa8ad0648c2b8de9473ef8ac/pimonitor/ui/__init__.py -------------------------------------------------------------------------------- /res/subaru_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elewarr/PiMonitor/8e425c3ca725355faa8ad0648c2b8de9473ef8ac/res/subaru_logo.png -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo PYTHONPATH=. python2.7 /home/pi/devel/PiMonitor/pimonitor/PMMain.py $1 3 | --------------------------------------------------------------------------------