├── .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 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
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 |
4 |
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 |
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 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 | 1406746534774
368 |
369 |
370 | 1406746534774
371 |
372 |
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 |
420 |
421 |
422 |
423 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 | file://$PROJECT_DIR$/pimonitor/ui/PMWindow.py
445 | 58
446 |
447 |
448 |
449 | file://$PROJECT_DIR$/pimonitor/cu/PMCUCalculatedParameter.py
450 | 31
451 |
452 |
453 |
454 | file://$PROJECT_DIR$/pimonitor/ui/PMWindow.py
455 | 94
456 |
457 |
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 |
--------------------------------------------------------------------------------