etherage 0.5.1

An EtherCAT master in pure-Rust very close to the ethercat nature
Documentation
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
368
369
370
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
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
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
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
/*!
    structs and consts for every registers in a standard slave's RAM. This should be used instead of any hardcoded register value.

    The goal of this file is to gather all physical memory registers at one place, so what you see here is exactly what you can expect in a slave, no more, no less.

    Some registers are partially redundant, this is because we can use some field pointing to a big struct and other fields pointing to only parts of the same struct.
*/

use core::fmt;
use bilge::prelude::*;
use crate::data::{self, Field, BitField, Storage};



pub mod address {
    use super::*;

    /// register of the station address, aka the fixed slave address
    /// ETG.1000.4 table 32
    pub const fixed: Field<u16> = Field::simple(0x0010);
    /// slave address alias
    /// ETG.1000.4 table 32
    pub const alias: Field<u16> = Field::simple(0x0012);
}
pub mod dl {
    use super::*;

    pub const information: Field<DLInformation> = Field::simple(0x0000);
	pub const control: Field<DLControl> = Field::simple(0x0100);
	pub const status: Field<DLStatus> = Field::simple(0x0110);
}

pub mod dls_user {
    use super::*;

    /// ETG.1000.4 table 35
    pub const r1: Field<u8> = Field::simple(0x0120);
    pub const r2: Field<u8> = Field::simple(0x0121);
    pub const r3: Field<u8> = Field::simple(0x0130);
    pub const r4: Field<u8> = Field::simple(0x0131);
    pub const r5: Field<u16> = Field::simple(0x0132);
    pub const r6: Field<u16> = Field::simple(0x0134);
    pub const r7: Field<u8> = Field::simple(0x0140);
    pub const copy_r1_r3: BitField<bool> = BitField::new(0x0141*8, 1);
    pub const r9: BitField<u8> = BitField::new(0x0141*8+1, 7);
    pub const r8: Field<u8> = Field::simple(0x0150);
    
    /// ETG.1000.4 table 61
    pub const p1: Field<u8> = Field::simple(0x0981);
    pub const p2: Field<u16> = Field::simple(0x0982);
    pub const p13: Field<u8> = Field::simple(0x0982);
    pub const p14: Field<u8> = Field::simple(0x0984);
    pub const p3: Field<u16> = Field::simple(0x098e);
    pub const p4: Field<u32> = Field::simple(0x0990);
    pub const p5: Field<u32> = Field::simple(0x09a0);
    pub const p6: Field<u32> = Field::simple(0x09a4);
    pub const p7: Field<u16> = Field::simple(0x09a8);
    pub const p8: Field<u16> = Field::simple(0x09ae);
    pub const p9: Field<u32> = Field::simple(0x09b0);
    pub const p10: Field<u32> = Field::simple(0x09b8);
    pub const p11: Field<u32> = Field::simple(0x09c0);
    pub const p12: Field<u32> = Field::simple(0x09c8);

    /// ETG.1000.4 table 36
    pub const event: Field<DLSUserEvents> = Field::simple(0x0220);
    /// ETG.1000.4 table 37
    /// 0 to disable the matching event in [event], 1 to enable
    pub const event_mask: Field<DLSUserEvents> = Field::simple(0x0202);
    pub const watchdog: Field<u16> = Field::simple(0x0410);
}

pub const external_event: Field<ExternalEvent> = Field::simple(0x0210);
pub const external_event_mask: Field<ExternalEvent> = Field::simple(0x0200);

pub const ports_errors: Field<PortsErrorCount> = Field::simple(0x0300);
pub const lost_link_count: Field<LostLinkCount> = Field::simple(0x0310);
pub const frame_error_count: Field<FrameErrorCount> = Field::simple(0x0308);
pub const watchdog_divider: Field<u16> = Field::simple(0x0400);
pub const watchdog_counter: Field<WatchdogCounter> = Field::simple(0x0442);


/**
    SM (Sync Managers) are used for configuring and controling two distinct things:
    - mailbox exchanges (CoE, FoE, ...)
    - pdo exchanges (copying PDO data to slave's physical memory)
*/
pub mod sync_manager {
    use super::*;

    /// ETG.1000.4 table 45
    pub const watchdog: Field<u16> = Field::simple(0x0420);
    /// ETG.1000.4 table 46
    /// true when the watchdog has not expired
    pub const watchdog_status: Field<bool> = Field::simple(0x0440);
	pub const interface: SyncManager = SyncManager {address: 0x0800, num: 16};
}

/// SII (Slave Information Interface) allows to retreive declarative informations about a slave (like a manifest) like product code, vendor, etc as well as slave boot-up configs
/// ETG.1000.4.6.6.4
pub mod sii {
    use super::*;

    /// ETG.1000.4 table 48
	pub const access: Field<SiiAccess> = Field::simple(0x0500);
    /// ETG.1000.4 table 49
	pub const control: Field<SiiControl> = Field::simple(0x0502);
	/// register contains the address in the slave information interface which is accessed by the next read or write operation (by writing the slave info rmation interface control/status register).
    /// ETG.1000.4 table 50
	pub const address: Field<u16> = Field::simple(0x0504);
	/// register contains the data (16 bit) to be written in the slave information interface with the next write operation or the read data (32 bit/64 bit) with the last read operation.
    /// ETG.1000.4 table 51
	pub const data: Field<[u8; 8]> = Field::simple(0x0508);
	
	/// agregates [const@control] and [const@address] for optimized bandwith
	pub const control_address: Field<SiiControlAddress> = Field::simple(control.byte);
	/// agregates [const@control] and [const@address] and [const@data] for optimized bandwith
	pub const control_address_data: Field<SiiControlAddressData> = Field::simple(control.byte);
}

// TODO: MII (Media Independent Interface)

/// FMMU (Fieldbus Memory Management Unit) is controling the mapping (copy) for a slave's physical memory from/to logical memory
pub const fmmu: FMMU = FMMU {address: 0x0600, num: 16};

/// AL (Application Layer) registers are controling the communication state of a slave
pub mod al {
    use super::*;

    pub const control: Field<AlControlRequest> = Field::simple(dls_user::r1.byte);
    pub const response: Field<AlControlResponse> = Field::simple(dls_user::r3.byte);
    pub const error: Field<AlError> = Field::simple(dls_user::r6.byte);
    pub const status: Field<AlStatus> = Field::simple(dls_user::r3.byte);
    pub const pdi: Field<AlPdiControlType> = Field::simple(dls_user::r7.byte);
    pub const sync_config: Field<AlSyncConfig> = Field::simple(dls_user::r8.byte);
}

/**
	registers controlling the distributed clock. See [DistributedClock]
	
	ETG.1000.4.6.8
*/
pub mod dc {
    use super::*;

    /// full structure gathering all the dc registers
    pub const all: Field<DistributedClock> = Field::simple(0x0900);
    // direct access to DC struct fields
    /// accedd to the first item of [received_time], a write access to it latches the local time to update the fields of the DC
    pub const measure_time: Field<u32> = Field::simple(0x0900);
    /**
        A write access to port 0 latches the local time (in ns) at receive begin (start first element of preamble) on each port of this PDU in this parameter (if the PDU was received correctly).
        This array contains the latched receival time on each port.
    */
    pub const received_time: Field<[u32; 4]> = Field::simple(0x0900);
    /// A write access compares the latched local system time (in ns) at receive begin at the processing unit of this PDU with the written value (lower 32 bit; if the PDU was received correctly), the result will be the input of DC PLL
    pub const system_time: Field<u64> = Field::simple(0x0910);
    /// Local time (in ns) at receive begin at the processing unit of a PDU containing a write access to Receive time port 0 (if the PDU was received correctly)
    pub const local_time: Field<u64> = Field::simple(0x0918);
    /// Offset between the local time (in ns) and the local system time (in ns)
    pub const system_offset: Field<u64> = Field::simple(0x920);
    /// Offset between the reference system time (in ns) and the local system time (in ns)
    pub const system_delay: Field<u32> = Field::simple(0x928);
    /**
        Bits 30..0: Mean difference between local copy of "system time" and "received system time" values.
        Bit 31: 
            - 0 - Local copy of "system time" > "received system time". 
            - 1 - Othercase
    */
    pub const system_difference: Field<TimeDifference> = Field::simple(0x092C);
    /// Implementation specific
    pub const param_0: Field<u16> = Field::simple(0x0930);
    pub const param_2: Field<u16> = Field::simple(0x0934);
}

/**
    registers allowing to trigger slave cyclic task using to the distributed clock ticks or ethercat frames. See [Isochronous]
    
    ETG.1000.5.6.1.3.2.2
*/
pub mod isochronous {
    use super::*;
    
    /// full structure gathering all the isochronous registers, without the mess of ETG unordered registers
    pub const all: Field<Isochronous> = Field::simple(0x0980);
    
    /// ETG.1000.6 table 27
    pub mod sync {
        use super::*;
        
        /// boolean flags enabling sync operations (RW)
        pub const enable: Field<IsochronousEnables> = Field::simple(dls_user::p1.byte);
        /// This optional attribute specifies the duration for the Sync Impulse in multiples of 10 ns. (R)
        pub const pulse: Field<u16> = Field::simple(dls_user::p2.byte);
        /// These optional booleans attribute indicates an active Sync interrupts. (R)
        pub const interrupt0: Field<IsochronousInterrupt> = Field::simple(dls_user::p3.byte);
        pub const interrupt1: Field<IsochronousInterrupt> = Field::simple(dls_user::p3.byte+1);
        /**
            This optional attribute sets a start time related to system time for cyclic operation. (RW)
            
            The interrupt generation will start when the lower 32 bits of the system time will reach this value (in ns)
        */
        pub const start_time: Field<u32> = dls_user::p4;
        /// set the cycle time of Sync0 in multiples of 1ns for Sync0 (RW)
        pub const sync0_cycle_time: Field<u32> = Field::simple(dls_user::p5.byte);
        /// set the cycle time of Sync0 in multiples of 1ns for Sync1 (RW)
        pub const sync1_cycle_time: Field<u32> = Field::simple(dls_user::p6.byte);
    }
    /// ETG.1000.6 table 28
    pub mod latch {
        use super::*;
        
        /// enables Latch operation for a single event (true) or continuous latching. (RW)
        pub const edge0: Field<IsochronousLatch> = Field::simple(dls_user::p7.byte);
        pub const edge1: Field<IsochronousLatch> = Field::simple(dls_user::p7.byte+1);
        /// indicates Latch edge events. (R)
        pub const event0: Field<IsochronousLatch> = Field::simple(dls_user::p8.byte);
        pub const event1: Field<IsochronousLatch> = Field::simple(dls_user::p8.byte+1);
        /// stores the system time value in case of a Latch edge events. (R)
        pub mod times {
            use super::*;
        
            pub const latch0_positive: Field<u32> = Field::simple(dls_user::p9.byte);
            pub const latch0_negative: Field<u32> = Field::simple(dls_user::p10.byte);
            pub const latch1_positive: Field<u32> = Field::simple(dls_user::p11.byte);
            pub const latch1_negative: Field<u32> = Field::simple(dls_user::p12.byte);
        }
    }
}






/// ETG.1000.6 table 9
#[bitsize(8)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Eq, PartialEq, Default)]
pub struct AlControlRequest {
    /// requested state of communication
    pub state: AlMixedState,
    /// if true, parameter change of the [AlStatus::changed] will be reset
    pub ack: bool,
    /// request of id instead of error code in [al::error]
    pub request_id: bool,
    reserved: u2,
}
data::bilge_pdudata!(AlControlRequest, u8);

/// ETG.1000.6 table 10
#[bitsize(8)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Eq, PartialEq)]
pub struct AlControlResponse {
    /// formerly requested state of communication
    pub state: AlMixedState,
    /**
    - false: State transition successful
    - true: State transition not successful
    */
    pub error: bool,
    /// if true, ID value is present in R6
    pub id: bool,
    reserved: u2,
}
data::bilge_pdudata!(AlControlResponse, u8);

/// ETG.1000.6 table 12
#[bitsize(8)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Eq, PartialEq)]
pub struct AlStatus {
    /// current state of communication
    pub state: AlMixedState,
    /// true if requested by [AlControlRequest::ack]
    pub changed: bool,
    reserved: u3,
}
data::bilge_pdudata!(AlStatus, u8);

/**
    the current operation state on one device.

    This is the enum version, useful when communicating with one slave only

    Except [Self::Bootstrap], changing to any mode can be requested from any upper mode or from the preceding one.
    
    ![communication state](https://raw.githubusercontent.com/jimy-byerley/etherage/master/schemes/communication-state.svg)

    ETG.1000.6 table 9
*/
#[bitsize(4)]
#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq)]
pub enum AlState {
    /**
        Transitional state meaning the slave is booting up and ready for nothing yet. The slave should normally reach the [Self::Init] state within seconds.

        It cannot be requested, nor changed while it is active.
    */
    Bootstrap = 3,
    /**
        The init mode allows to set many communication registers, like the salve address, the mailbox setup, etc.

        This mode should be used at the beginning of a communication. Only registers can be used.
    */
    Init = 1,
    /**
        the pre operational mode allows mailbox communication, which is mendatory to configure some slaves before realtime operations. Most functions are enabled but not realtime.

        Communication setup via registers is no more allowed in this mode.
    */
    PreOperational = 2,
    /**
        Mode allowing realtime operations, except that commands sent to the slaves via its mapping will not be executed.

        This is a kind of read-only temporary mode before [Self::Operational], that can be useful for initializing control loops on the master side while their outputs are ignored.

        Mapping is no more allowed in this state, nor communication setup via registers.
    */
    SafeOperational = 4,
    /**
        Realtime operations running

        The master has full access to the slave's effector functions. slaves might expect the master to regularly refresh its commands.

        Mapping is no more allowed in this state, nor communication setup via registers.
    */
    Operational = 8,
}

/**
	gather the current operation states on several devices
	this struct does not provide any way to know which slave is in which state

	This is the bitfield version, useful when communicating with multiple slaves (broadcast PDUs)

    ETG.1000.6 table 9
*/
#[bitsize(4)]
#[derive(FromBits, DebugBits, Copy, Clone, Eq, PartialEq, Default)]
pub struct AlMixedState {
    /// one slave at least is in [AlState::Init]
	pub init: bool,
	/// one slave at least is in [AlState::PreOperational]
	pub pre_operational: bool,
	/// one slave at least is in [AlState::SafeOperational]
	pub safe_operational: bool,
	/// one slave at least is in [AlState::Operational]
	pub operational: bool,
}

impl fmt::Display for AlMixedState {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}{{", core::any::type_name::<Self>()) ?;
		for (active, mark) in [ (self.init(), "init"),
								(self.pre_operational(), "pre"),
								(self.safe_operational(), "safe"),
								(self.operational(), "op"),
								] {
			write!(f, " ")?;
			if active {
				write!(f, "{}", mark)?;
			} else {
				for _ in 0 .. mark.len() {write!(f, " ")?;}
			}
		}
		write!(f, "}}")?;
		Ok(())
	}
}

impl TryFrom<AlMixedState> for AlState {
    type Error = &'static str;
    fn try_from(state: AlMixedState) -> Result<Self, Self::Error> {
        Self::try_from(u4::from(state)).map_err(|e|  "cannot unwrap when not only 1 state in mix")
    }
}
impl From<AlState> for AlMixedState {
    fn from(state: AlState) -> Self {
        Self::from(u4::from(state))
    }
}

/// ETG.1000.6 table 11
#[bitsize(16)]
#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq)]
pub enum AlError {
   ///  No error Any Current state
    NoError = 0x0000,
    ///  Unspecified error
    Unspecified = 0x0001,
    ///  No Memory
    NoMemory = 0x0002,
    ///  Invalid Device Setup
    InvalidDeviceSetup = 0x0003,
//     0x0005 Reserved due to compatibility reasons
    ///  Invalid requested state change
    InvalidStateRequest = 0x0011,
    ///  Unknown requested state
    UnknownStateRequest = 0x0012,
    ///  Bootstrap not supported
    BootstrapNotSupported = 0x0013,
    ///  No valid firmware
    NoValidFirmware = 0x0014,
    ///  Invalid mailbox configuration for switching to [AlState::Init]
    InvalidMailboxConfigBoot = 0x0015,
    ///  Invalid mailbox configuration for switching to [AlState::PreOperational]
    InvalidMailboxConfigPreop = 0x0016,
    ///  Invalid sync manager configuration
    InvalidSyncConfig = 0x0017,
    ///  No valid inputs available
    NoInputsAvailable = 0x0018,
    ///  No valid outputs
    NoValidInputs = 0x0019,
    ///  Synchronization error
    Synchronization = 0x001A,
    ///  Sync manager watchdog
    SyncWatchdog = 0x001B,
    ///  Invalid Sync Manager Types
    InvalidSyncTypes = 0x001C, 
    /**  
        Invalid Output Configuration
        
        raise when a something is wrong in a sync channel or PDO mapping that should be written by the master
    */
    InvalidOutputConfig = 0x001D, 
    /**  
        Invalid Input Configuration
        
        raise when a something is wrong in a sync channel or PDO mapping that should be read by the master
    */
    InvalidInputConfig = 0x001E, 
    ///  Invalid Watchdog Configuration
    InvalidWatchdogConfig = 0x001F,
    ///  Slave needs cold start
    NeedColdStart = 0x0020,
    ///  Slave needs INIT
    NeedInit = 0x0021,
    ///  Slave needs PREOP
    NeedPreop = 0x0022,
    ///  Slave needs SAFEOP
    NeedSafeOp = 0x0023,
    ///  Invalid Input Mapping
    InvalidInputMapping = 0x0024,
    ///  Invalid Output Mapping
    InvalidOutputMapping = 0x0025,
    ///  Inconsistent Settings
    InconsistentSettings = 0x0026,
    ///  FreeRun not supported
    FreeRunNotSupported = 0x0027,
    ///  SyncMode not supported
    SyncModeNotSupported = 0x0028,
    ///  FreeRun needs 3Buffer Mode
    FreeRunNeedsBuffer = 0x0029,
    ///  Background Watchdog
    BackgroundWatchdog = 0x002A,
    ///  No Valid Inputs and Outputs
    NoValidIO = 0x002B,
    ///  Fatal Sync Error
    FatalSync = 0x002C,
    ///  No Sync Error
    /// SyncSignal not received: In SAFEOP the slave waits for the first Sync0/Sync1 events before switching to OP, if these events were not received during the SAFEOP to OP timeout the slave refuses the state transition to OP
    NoSync = 0x002D,
    ///  Invalid DC SYNC Configuration
    InvalidDcConfig = 0x0030,
    ///  Invalid DC Latch Configuration
    InvalidLatchConfig = 0x0031,
    ///  Phase Link Lock Error
    PLL = 0x0032,
    ///  Distributed Clock Sync IO Error
    DCSyncIO = 0x0033,
    ///  Distributed Clock Sync Timeout Error
    DCSyncTimeout = 0x0034,
    ///  Distributed Clock Invalid Sync Cycle Time
    DCInvalidPeriod = 0x0035,
// 0x0036 Distributed Clock Sync0 Cycle Time
// 0x0037 Distributed Clock Sync1 Cycle Time
    ///  MBX_AOE
    MailboxAOE = 0x0041,
    ///  MBX_EOE
    MailboxEOE = 0x0042,
    ///  MBX_COE
    MailboxCOE = 0x0043,
    ///  MBX_FOE
    MailboxFOE = 0x0044,
    ///  MBX_SOE
    MailboxSOE = 0x0045,
    ///  MBX_VOE
    MailboxVOE = 0x004F,
    ///  raised when switching to PreOperational but SII access owner has not been switched to PDI
    EepromNoAccess = 0x0050,
    ///  EEPROM Error
    Eeeprom = 0x0051,
    ///  Slave restarted locally
    SlaveRestarted = 0x0060,
    ///  Device Identification value updated
    DeviceIdentificationUpdated = 0x0061,
    // 0x0062 …0  reserved
    ///  Application controller available
    ApplicationAvailable = 0x00F0,
    // 0x00F0 - 0xFFFF:  reserved
    // 0x8000 - 0xFFFF:  vendor specific
    Specific = 0xffff,
}
data::bilge_pdudata!(AlError, u16);

/// ETG.1000.6 table 13
#[bitsize(9)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Eq, PartialEq, Default)]
pub struct AlPdiControlType {
    /// Type specific (see ETG.1000.3 DL information parameter)
    pub pdi: u8,
    /**
        - false: AL Management will be done by an application Controller
        - true: AL Management will be emulated (AL status follows directly AL control)
    */
    pub strict: bool,
}
data::bilge_pdudata!(AlPdiControlType, u9);

/// ETG.1000.6 table 15
#[bitsize(8)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Eq, PartialEq, Default)]
pub struct AlSyncConfig {
    /// controller specific
    pub signal_conditioning_sync0: u2,
    pub enable_signal_sync0: bool,
    pub enable_interrupt_sync0: bool,

    /// controller specific
    pub signal_conditioning_sync1: u2,
    pub enable_signal_sync1: bool,
    pub enable_interrupt_sync1: bool,
}
data::bilge_pdudata!(AlSyncConfig, u8);


/// ETG.1000.4 table 31
#[bitsize(80)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Eq, PartialEq)]
pub struct DLInformation {
    /// type of the slave controller
    pub ty: u8,
    /// (major revision) revision of the slave controller.
    pub revision: u8,
    /// (minor revision) build number of the slave controller
    pub build: u16,

    /// Number of supported FMMU entities  (1 to 10)
    pub fmmus: u8,
    /// Number of supported Sync Manager channels (1 to 10)
    pub sync_managers: u8,

    /// RAM size in koctet, means 1024 octets (1-60)
    pub ram_size: u8,
    /// tells which port are present on a slave
    pub ports: [PortDescriptor; 4],

    /**
        - `false`: bit operation supported
        - `true`: bit operation not supported This feature bit does not affect mappability of SM.WriteEvent flag (MailboxIn)
    */
    pub fmmu_bit_operation_not_supported: bool,
    /**
        - `true`: shall only be used for legacy reasons. Reserved registers may not be written, reserved registers may not be read when out of register area (refer to documentation of specific slave controller (ESC)
        - `false`: no restriction in register access
    */
    pub reserved_registers_not_supported: bool,
    pub dc_supported: bool,
    pub dc_range: DcRange,
    pub ebus_low_jitter: bool,
    pub ebus_enhanced_link_detection: bool,
    /// true if available
    pub mii_enhanced_link_detection: bool,
    /// if true, frames with modified FCS (additional nibble) should be counted separately in RX-Error Previous counter
    pub separate_fcs_errors: bool,
    /// true if available. This feature refers to registers 0x981\[7:3\], 0x0984
    pub dc_sync_activation_enhanced: bool,

    /// if true, `LRW` is not supported
    pub logical_exchange_not_supported: bool,
    /// if true, `BRW`, `APRẀ`, `FPRW` is not supported
    pub physicial_exchange_not_supported: bool,

    /**
        - 0: not active
        - 1: active, FMMU 0 is used for RxPDO (no bit mapping) FMMU 1 is used for TxPDO (no bit mapping) FMMU 2 is used for Mailbox write event bit of Sync manager 1 Sync manager 0 is used for write mailbox Sync manager 1 is used for read mailbox Sync manager 2 is used as Buffer for incoming data Sync manager 3 is used as Buffer for outgoing data
    */
    pub special_fmmu_config: bool,
    reserved: u4,
}
data::bilge_pdudata!(DLInformation, u80);

#[bitsize(2)]
#[derive(FromBits, Debug, Copy, Clone)]
pub enum PortDescriptor {
    NotImplemented = 0b00,
    NotConfigured = 0b01,
    /// ethernet bus
    Ebus = 0b10,
    /// Machine Abstraction Interface (MII/RMII)
    Mii = 0b11,
}
#[bitsize(1)]
#[derive(FromBits, Debug, Copy, Clone)]
pub enum DcRange {
    /// default
    Bit32 = 0,
    /// 64 bit for system time, system time offset and receive time processing unit
    Bit64 = 1,
}



/// used by the slave to inform the master which mailbox protocl can be used with the slave.
/// ETG.1000.6 table 18
#[bitsize(16)]
#[derive(TryFromBits, DebugBits, Copy, Clone)]
pub struct MailboxSupport {
    /// ADS over EtherCAT (routing and parallel services)
    pub aoe: bool,
    /// Ethernet over EtherCAT (tunnelling of Data Link services)
    pub eoe: bool,
    /// CAN application protocol over EtherCAT (access to SDO)
    pub coe: bool,
    /// File Access over EtherCAT
    pub foe: bool,
    /// Servo Drive Profile over EtherCAT
    pub soe: bool,
    /// Vendor specific protocol over EtherCAT
    pub voe: bool,
    reserved: u10,
}
data::bilge_pdudata!(MailboxSupport, u16);



/**
	control the operation of the DL ports of the slave controller by the master.

	ETG.1000.4 table 33
*/
#[bitsize(32)]
#[derive(TryFromBits, DebugBits, Copy, Clone)]
pub struct DLControl {
	/**
		enables forwarding non-ethercat frames.
		Restricted forwarding will destroy non EtherCAT frames
	*/
	forwarding: Forwarding,
	/**
		This optional parameter enables temporary use of the loop control parameters written in the same frame for about one second. After this timeout, the original Loop control settings are restored automatically
		
		- false: permanent setting
		- true: temporary use of Loop Control Settings for ~1 second
	*/
	temporary: bool,
	reserved: u6,
	/// if there is an automatic activation of the port in case of a physical link or if the port is opened and or closed by commands of the master
	ports: [LoopControl; 4],
	/**
		Buffer between preparation and send. Send will be if buffer is half full (7).
		
		This optional parameter should be used to optimize the delay within a station. If this station and its neighbours have a stable rate of transmitting, this parameter may be reduced. The default settings are determined by the required clock accuracy of ISO/IEC 8802-3.
	*/
	transmit_buffer_size: u3,
	/** 
		This optional parameter indicates that the reduction of frame forwarding jitter for EBUS is enabled.
		set to true to activate
	*/
	low_jitter_ebus: bool,
	reserved: u4,
	/// should be used to enable the alias name
	alias_enable: bool,
	reserved: u7,
}
data::bilge_pdudata!(DLControl, u32);

#[bitsize(1)]
#[derive(TryFromBits, Debug, Copy, Clone)]
pub enum Forwarding {
	/// EtherCAT frames are processed, non-EtherCAT frames are forwarded without modification, The source MAC address is not changed for any frame
	Transmit = 0,
	/// EtherCAT frames are processed, non-EtherCAT frames are destroyed, The source MAC address is changed by the Processing Unit for every frame (SOURCE_MAC\[1\] is set to 1 – locally administered address).
	Filter = 1,
}
data::bilge_pdudata!(Forwarding, u1);

#[bitsize(2)]
#[derive(TryFromBits, Debug, Copy, Clone)]
pub enum LoopControl {
	/// closed at “link down”, open with “link up”
	Auto = 0,
	/// loop closed at “link down”, opened with writing 01 after “link up” (or receiving a valid Ethernet frame at the closed port)
	AutoClose = 1,
	AlwaysOpen = 2,
	AlwaysClosed = 3,
}
data::bilge_pdudata!(LoopControl, u2);

/**
	indicate the state of the DL ports and the state of the interface between DL-user and DL.
	
	ETG.1000.4 table 34
*/
#[bitsize(16)]
#[derive(FromBits, DebugBits, Copy, Clone)]
pub struct DLStatus {
	/** 
		if a DL-user is connected to the process data interface of the slave controller.
		true if operational
	*/
	pub dls_user_operational: bool,
	/// true if watchdog not expired
	pub dls_user_watchdog: bool,
	/**
		status of the activation of the extended link detection.
		true if activated for at least one port
	*/
	pub extended_link_detection: bool,
	reserved: u1,
	/// indicates physical link on each port
	pub port_link_status: [bool; 4],
	/// indicates closed loop link status on each port
	pub port_loop_status: [LoopStatus; 4],
}
data::bilge_pdudata!(DLStatus, u16);

/// ETG.1000.4 table 34
#[bitsize(2)]
#[derive(FromBits, DebugBits, Copy, Clone)]
pub struct LoopStatus {
	/// indicates forwarding on the same port i.e. loop back.
	pub loop_back: bool,
	///  signal detected on the port's RX
	pub signal_detection: bool,
}
data::bilge_pdudata!(LoopStatus, u2);

/// The event registers are used to indicate an event to the DL -user. The event shall be acknowledged if the corresponding event source is read. The events can be masked.
#[bitsize(32)]
#[derive(FromBits, DebugBits, Copy, Clone, Default)]
pub struct DLSUserEvents {
	/// R1 was written
	pub r1_change: bool,
	pub dc: [bool; 3],
	/// event active (one or more Sync manager channels were changed)
	pub sync_manager_change: bool,
	/// EEPROM command pending
	pub eeprom_emulation: bool,
	reserved: u2,
	/// mark whether each sync manager channel was accessed
	pub sync_manager_channel: [bool; 16],
	reserved: u8,
}
data::bilge_pdudata!(DLSUserEvents, u32);

/**
	The external event is mapped to IRQ parameter of all EtherCAT PDUs accessing this slave. If an event is set, and the associated mask is set the corresponding bit in the IRQ parameter of a PDU is set.

	ETG.1000.4 table 38
*/
#[bitsize(16)]
#[derive(FromBits, DebugBits, Copy, Clone, Default)]
pub struct ExternalEvent {
	/// dc event 0
	pub dc0: bool,
	reserved: u1,
	/// dl status register was changed
	pub dl: bool,
	/// R3 or R4 was written, meaning an [AlStatus] change
	pub al: bool,
	/// sync manager channel was accessed by slave
	pub sync_manager_channel: [bool; 8],
	reserved: u4,
}
data::bilge_pdudata!(ExternalEvent, u16);

/// A write to one counter will reset all counters of the group
/// ETG.1000.4 table 40
#[repr(packed)]
#[derive(Clone, Debug, Default)]
pub struct PortsErrorCount {
	pub port: [PortErrorCount; 4],
}
data::packed_pdudata!(PortsErrorCount);

#[bitsize(16)]
#[derive(FromBits, DebugBits, Copy, Clone, Default)]
pub struct PortErrorCount {
	/// counts the occurrences of frame errors (including RX errors within frame)
	pub frame: u8,
	/// counts the occurrences of RX errors at the physical layer
	pub physical: u8,
}
data::bilge_pdudata!(PortErrorCount, u16);

#[bitsize(32)]
#[derive(FromBits, DebugBits, Copy, Clone)]
pub struct LostLinkCount {
	/// counts the occurrences of link down.
	pub port: [u8; 4],
}
data::bilge_pdudata!(LostLinkCount, u32);

/**
	A write to one counter will reset all Previous Error counters
*/
#[bitsize(48)]
#[derive(FromBits, DebugBits, Copy, Clone)]
pub struct FrameErrorCount {
	/**
	The optional previous indicate a problem on checksum this could be counter is written. The reached. error counter registers contain information about error frames that the predecessor links. As frames with error have a specific type of detected and reported. All previous error counters will be cleared if one counting is stopped for each counter whose maximum value (255) is reached.
	*/
	pub previous_error_count: [u8; 4],
	/// counts frames with i.e. wrong datagram structure. The counter will be cleared if the counter is written. The counting is stopped when the maximum value (255) is reached.
	pub malformat_frame_count: u8,
	/// counts occurrence of local problems. The counter will be cleared if the counter is written. The counting is stopped when the maximum value (255) is reached.
	pub local_problem_count: u8,
}
data::bilge_pdudata!(FrameErrorCount, u48);

/**
	A write will reset the watchdog counters

	ETG.1000.4 table 47
*/
#[bitsize(16)]
#[derive(FromBits, DebugBits, Copy, Clone)]
pub struct WatchdogCounter {
	/// counts the expiration of all Sync manager watchdogs.
	pub sync_manager: u8,
	/// counts the expiration of DL-user watchdogs.
	pub pdi: u8,
}
data::bilge_pdudata!(WatchdogCounter, u16);


/// ETH.1000.4 table 48
#[bitsize(16)]
#[derive(FromBits, DebugBits, Copy, Clone, Default)]
pub struct SiiAccess {
	pub owner: SiiOwner,
	/// setting this will reset access to SII
	pub lock: bool,
	pub reserved: u6,
	/// PDI access active
	pub pdi: bool,
	pub reserved: u7,
}
data::bilge_pdudata!(SiiAccess, u16);

/// the EEPROM can be accessed only through the SII, and this enum is used to share it between the master (through ethercat communication) and the slave itself
#[bitsize(1)]
#[derive(FromBits, Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum SiiOwner {
    /// the ethercat communication can controle the SII (and access the EEPROM)
    #[default]
	EthercatDL = 0,
	/// the slave can control the SII (and access the EEPROM)
	Pdi = 1,
}
data::bilge_pdudata!(SiiOwner, u1);

/**
    register controling the read/write operations to Slave Information Interface (SII)

	ETG.1000.4 table 49
*/
#[bitsize(16)]
#[derive(FromBits, DebugBits, Copy, Clone, Default)]
pub struct SiiControl {
	/// true if SII is writable
	pub write_access: bool,
	reserved: u4,
	/**
		- false: Normal operation (DL interfaces to SII)
		- true: DL-user emulates SII
		
		cannot be set by the master
	*/
	pub eeprom_emulation: bool,
	/// number of bytes per read transaction, cannot be set by master
	pub read_size: SiiTransaction,
	/// unit of SII addresses, cannot be set by master
	pub address_unit: SiiUnit,

	/**
		read operation requested (parameter write) or read operation busy (parameter read)
		To start a new read operation there must be a positive edge on this parameter
		
		This parameter will be written from the master to start the read operation of 32 bits/64 bits in the slave information interface. This parameter will be read from the master to check if the read operation is finished. 
	*/
	pub read_operation: bool,
	/**
		write operation requested (parameter write) or write operation busy (parameter read)
		To start a new write operation there must be a positive edge on this parameter
		
		This parameter will be written from the master to start the write operation of 16 bits in the slave information interface. This parameter will be read from the master to check if the write operation is finished. There is no consistence gu arantee for write operation. A break down during write can produce inconsistent values and should be avoided. 
	*/
	pub write_operation: bool,
	/**
		reload operation requested (parameter write) or reload operation busy (parameter read)
		To start a new reload operation there must be a positive edge on this parameter
		
		This parameter will be written from the master to start the reload operation of the first 128 bits in the slave information interface. This parameter will be read from the master to check if the reload operation is finished
	*/
	pub reload_operation: bool,

	/// checksum error while reading at startup
	pub checksum_error: bool,
	/// error on reading Device Information
	pub device_info_error: bool,
	/**
        error on last SII request
        
        writable only in SII emulation mode
    */
	pub command_error: bool,
	/// error on last write operation
	pub write_error: bool,

	/// operation is ongoing
	pub busy: bool,
}
data::bilge_pdudata!(SiiControl, u16);

#[bitsize(1)]
#[derive(FromBits, Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum SiiTransaction {
    #[default]
	Bytes4 = 0,
	Bytes8 = 1,
}
#[bitsize(1)]
#[derive(FromBits, Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum SiiUnit {
    #[default]
	Byte = 0,
	Word = 1,
}

#[repr(packed)]
#[derive(Debug, Copy, Clone)]
pub struct SiiControlAddress {
    pub control: SiiControl,
    pub address: u16,
}
data::packed_pdudata!(SiiControlAddress);

#[repr(packed)]
#[derive(Debug, Copy, Clone)]
pub struct SiiControlAddressData {
    pub control: SiiControl,
    pub address: u16,
    pub reserved: u16,
    pub data: [u8; 2],
}
data::packed_pdudata!(SiiControlAddressData);

/// this is not a PduData but a struct transporting the address and number of FMMU registers
/// ETG.1000.4 table 57
pub struct FMMU {
    /// address of the first entry
	pub address: u16,
	/// number of entries
	pub num: u8,
}

impl FMMU {
    /// return an entry of the FMMU
    pub fn entry(&self, index: u8) -> Field<FmmuEntry>  {
        assert!(index < self.num, "index out of range");
        Field::simple(usize::from(self.address + u16::from(index)*0x10))
    }
}

/**
	The fieldbus memory management unit (FMMU) converts logical addresses into physical addresses by the means of internal address. Thus, FMMUs allow one to use logical addressing for data segments that span several slave devices: one DLPDU addresses data within several arbitrarily distributed devices. The FMMUs optionally support bit wise mapping. A DLE may contain several FMMU entities. Each FMMU entity maps one cohesive logical address space to one cohesive physical address space.

	The FMMU consists of up to 16 entities. Each entity describes one memory translation between the logical memory of the EtherCAT communication network and the physical memory of the slave.

	ETG.1000.4 table 56
*/
#[bitsize(128)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Default)]
pub struct FmmuEntry {
	/// start byte in the logical memory
	pub logical_start_byte: u32,
	/// byte size of the data (rounded to lower value in case of bit-sized data ?)
	pub logical_len_byte: u16,
	/// offset of the start bit in the logical start byte
	pub logical_start_bit: u3,
	reserved: u5,
	/// offset of the end bit in the logical start byte
	pub logical_end_bit: u3,
	reserved: u5,

	/// start byte in the physical memory (set by the sync manager)
	pub physical_start_byte: u16,
	/// start bit in the physical start byte
	pub physical_start_bit: u3,
	reserved: u5,

	/// entity will be used for read service
	pub read: bool,
	/// entity will be used for write service
	pub write: bool,
	reserved: u6,

	/// enable this FMMU entry, so physical memory will be copied from/to logical memory on read/write
	pub enable: bool,
	reserved: u7,
	reserved: u24,
}
data::bilge_pdudata!(FmmuEntry, u128);

/// this is not a PduData but a convenience struct transporting the addresses of a sync manager
/// ETG.1000.4 table 59
pub struct SyncManager {
    /// start address of the sync manager (address of the first channel)
    pub address: u16,
    /// number of channels
    pub num: u8,
}

impl SyncManager {
    /// return one sync manager channel register
    pub fn channel(&self, index: u8) -> Field<SyncManagerChannel> {
        assert!(index < self.num, "index out of range");
        Field::simple(usize::from(self.address + u16::from(index) * (core::mem::size_of::<SyncManagerChannel>() as u16) ))
    }
    /// return the sync manager channel recommended for mailbox in
    pub fn mailbox_write(&self) -> Field<SyncManagerChannel>   {self.channel(0)}
    /// return the sync manager channel recommended for mailbox out
    pub fn mailbox_read(&self) -> Field<SyncManagerChannel>   {self.channel(1)}
    
    /// return the sync manager channel recommended for mapping to logical in
    pub fn logical_write(&self) -> Field<SyncManagerChannel>  {self.channel(2)}
    /// return the sync manager channel recommended for mapping to logical out
    pub fn logical_read(&self) -> Field<SyncManagerChannel>  {self.channel(3)}
    
    // return one of the sync manager channels reserved for mapping
    pub fn mappable(&self, index: u8) -> Field<SyncManagerChannel>   {self.channel(2+index)}
}

/**
    The Sync manager controls the access to the DL-user memory. Each channel defines a consistent area of the DL-user memory.

    There is two ways of data exchange between master and PDI:
    - Handshake mode (mailbox): one entity fills data in and cannot access the area until the other entity reads out the data.
    - Buffered mode: the interaction between both producer of data and consumer of data is uncorrelated – each entity expects access at any time, always providing the consumer with the newest data.

    ETG.1000.4 table 58
*/
#[bitsize(64)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Default)]
pub struct SyncManagerChannel {
    /// start address in octets in the physical memory of the consistent DL-user memory area.
    /// multiple sync manager channels with overlapping memory ranges are not supported.
    pub address: u16,
    /// size in octets of the consistent DL -user memory area.
    pub length: u16,
    /// whether the buffer is used for mailbox or exchange through mapping to the logical memory
    pub mode: SyncMode,
    /// whether the consistent DL -user memory area is read or written by the master.
    pub direction: SyncDirection,

    /// an event is generated if there is new data available in the consistent DL-user memory area which was written by the master (direction write) or if the new data from the DL-user was read by the master (direction read).
    pub ec_event: bool,
    /// an event is generated if there is new data available in the consistent DL-user memory area which was written by DLS-user or if the new data from the Master was read by the DLS-user.
    pub dls_user_event: bool,
    /// if the monitoring of an access to the consistent DL-user memory area is enabled.
    pub watchdog: bool,
    reserved: u1,
    /// if the consistent DL -user memory (direction write) has been written by the master and the event enable parameter is set.
    pub write_event: bool,
    /// if the consistent DL -user memory (direction read) has been read by the master and the event enable parameter is set.
    pub read_event: bool,
    reserved: u1,

    /// true if there is data waiting to be read (by master or slave) in the buffer
    pub mailbox_full: bool,
    /// state (buffer number, locked) of the consistent DL-user memory if it is of buffered access type.
    pub buffer_state: u2,
    pub read_buffer_open: bool,
    pub write_buffer_open: bool,

    /// activate this channel
    pub enable: bool,
    /// A change in this parameter indicates a repeat request. This is primarily used to repeat the last mailbox interactions.
    pub repeat: bool,
    reserved: u4,

    /// if the DC 0 Event shall be invoked in case of a EtherCAT write
    pub dc_event_bus: bool,
    /// if the DC 0 Event shall be invoked in case of a local write
    pub dc_event_local: bool,
    /// disable this channel for PDI access
    pub disable_pdi: bool,
    /// indicates a repeat request acknowledge. After setting the value of Repeat in the parameter repeat acknowledge.
    pub repeat_ack: bool,
    reserved: u6,
}
data::bilge_pdudata!(SyncManagerChannel, u64);

/// ETG.1000.4 table 58
#[bitsize(2)]
#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum SyncMode {
    #[default]
    Buffered = 0,
    Mailbox = 2,
}
/// ETG.1000.4 table 58
#[bitsize(2)]
#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum SyncDirection {
    /// sync manager buffer is read by the master
    #[default]
    Read = 0,
    /// sync manager buffer is written by the master
    Write = 1,
}




/**
	DC allows for very precise timing requirements and for using timing signals that can be generated independent of the communication cycle. Systems with not so high requirements on synchronization may be synchronized by sharing a service (preferable LRW or LRD or LWR) or using the same Ethernet frame for access to buffers.
	
	### delay measurement
	
	Delay measurement needs time stamping information which is related to a single frame. The slave just provides means for time stamping, the calculation of the delay is the task of the master.
	
	### local time values
	
	The local time parameters contains the local system time and parameter for the control loop which are dedicate to implement a control loop for coordinating the local system time with a global time.

	ETG.1000.4 table 60
*/
#[repr(packed)]
#[derive(Clone, Copy, Default, Debug, PartialEq)]
pub struct DistributedClock {
    /**
        A write access to port 0 latches the local time (in ns) at receive begin (start first element of preamble) on each port of this PDU in this parameter (if the PDU was received correctly).
        This array contains the latched receival time on each port.
    */
    pub received_time: [u32;4],
    /// A write access compares the latched local system time (in ns) at receive begin at the processing unit of this PDU with the written value (lower 32 bit; if the PDU was received correctly), the result will be the input of DC PLL
    pub system_time: u64,
    /// Local time (in ns) at receive begin at the processing unit of a PDU containing a write access to Receive time port 0 (if the PDU was received correctly)
    pub local_time: u64,
    /// Offset between the local time (in ns) and the local system time (in ns)
    pub system_offset: u64,
    /// Offset between the reference system time (in ns) and the local system time (in ns)
    pub system_delay: u32,
    /**
        Bits 30..0: Mean difference between local copy of "system time" and "received system time" values.
        Bit 31: 
            - 0 - Local copy of "system time" > "received system time". 
            - 1 - Othercase
    */
    pub system_difference: TimeDifference,
    /// Implementation specific
    pub control_loop_params: [u16; 3],
}
data::packed_pdudata!(DistributedClock);

/// ETG.1000.4 table 60
#[bitsize(32)]
#[derive(TryFromBits, DebugBits, Copy, Clone, Default, PartialEq)]
pub struct TimeDifference {
    /// Mean difference between local copy of System Time and received System Time values
    pub mean: u31,
    /// true if local copy of system time smaller than received system time (offset is negative)
    pub sign: bool,
}
data::bilge_pdudata!(TimeDifference, u32);

impl From<TimeDifference> for i32 {
    fn from(value: TimeDifference) -> i32  {
        u32::from(value.mean().value()) as i32 
            * if value.sign() {-1} else {1}
    }
}
impl From<i32> for TimeDifference {
    fn from(value: i32) -> TimeDifference {
        TimeDifference::new(u31::new(value.abs() as u32), value < 0)
    }
}



/**
    registers allowing to trigger slave cyclic task using to the distributed clock ticks or ethercat frames
	
	ETG.1000.6 table 27, ETG.1000.4 table 61
*/
#[repr(packed)]
#[derive(Clone, Default, Debug, PartialEq)]
pub struct Isochronous {
	// DC Cyclic unit control
    reserved1: IsochronousAccess,
	
	/// boolean flags enabling sync operations (RW)
    pub enable: IsochronousEnables,
    /// This optional attribute specifies the duration for the Sync Impulse in multiples of 10 ns. (R)
    pub pulse: u16,
    reserved2: [u8; 10],
    /// These optional booleans attribute indicates an active Sync interrupts. (R)
    pub interrupt0: IsochronousInterrupt,
    pub interrupt1: IsochronousInterrupt,
	/**
		This optional attribute sets a start time related to system time for cyclic operation. (RW)
		
		The interrupt generation will start when the lower 32 bits of the system time will reach this value (in ns)
	*/
    pub start_time: u32,
    reserved3: [u8; 12],
	/// set the cycle time of Sync0 in multiples of 1ns for Sync0 (RW)
    pub sync0_cycle_time: u32,
	/// set the cycle time of Sync0 in multiples of 1ns for Sync1 (RW)
    pub sync1_cycle_time: u32,
	
	/// enables Latch operation for a single event (true) or continuous latching. (RW)
    pub latch0_edge: IsochronousLatch,
    pub latch1_edge: IsochronousLatch,
    pub reserved4: [u8; 4],
	/// indicates Latch edge events. (R)
    pub latch0_event: IsochronousLatch,
    pub latch1_event: IsochronousLatch,
	/// stores the system time value in case of a Latch edge events. (R)
    pub latch0_time_positive: u32,
    reserved5: u32,
    pub latch0_time_negative: u32,
    reserved6: u32,
    pub latch1_time_positive: u32,
    reserved7: u32,
    pub latch1_time_negative: u32,
    reserved8: u32,
}
data::packed_pdudata!(Isochronous);

#[bitsize(8)]
#[derive(Copy, Clone, Default, DebugBits, PartialEq)]
pub struct IsochronousAccess{
    pub write_access_cyclic : bool,
    reserved : u3,
    pub write_access_latch0 : bool,
    pub write_access_latch1 : bool,
    reserved : u2,
}
data::bilge_pdudata!(IsochronousAccess, u8);

/// ETG.1000.6 table 27
#[bitsize(8)]
#[derive(Copy, Clone, Default, DebugBits, PartialEq)]
pub struct IsochronousEnables {
    /// This Boolean attribute enables cyclic Sync 0 or 1 operation.
    pub operation: bool,
    /// This Boolean attribute enables the Sync 0 operation
    pub sync0: bool,
    /// This Boolean attribute enables the Sync 1 operation
    pub sync1: bool,
    
    pub auto_activation : bool,
    pub start_time_ext : bool,
    pub start_time_check : bool,
    pub half_range : bool,
    pub debug_pulse : bool,
}
data::bilge_pdudata!(IsochronousEnables, u8);

/// ETG.1000.6 table 27
#[bitsize(8)]
#[derive(Copy, Clone, Default, DebugBits, PartialEq)]
pub struct IsochronousInterrupt {
    pub enable : bool,
    reserved : u7
}
data::bilge_pdudata!(IsochronousInterrupt, u8);

/// ETG.1000.6 table 28
#[bitsize(8)]
#[derive(Copy, Clone, Default, DebugBits, PartialEq)]
pub struct IsochronousLatch {
    pub positive: bool,
    pub negative: bool,
    reserved: u6
}
data::bilge_pdudata!(IsochronousLatch, u16);