This is a M version. No idea how it performs with that many records. I stuck in a Table.Buffer at one point where it seems to make sense
Basically, it creates a combined column of Product&Machine&Event, then offsets that one row downward. By comparing the offset to the current row value, we find where the breaks are and place an index. Fill down the index and you have something you can group on.
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Added Index" = Table.AddIndexColumn(Source, "Index", 0, 1, Int64.Type),
#"Added Custom" = Table.AddColumn(#"Added Index", "Combo", each [Product]&[Machine]&[Event Code]),
#"Changed Type" = Table.Buffer(Table.TransformColumnTypes(#"Added Custom",{{"Product", type text}, {"Machine", type text}, {"Event Code", type text}, {"DateTime Start", type datetime}, {"DateTime End", type datetime}, {"Duration (s)", Int64.Type}})),
// pull combined Product&Machine&Event cell from prior row
shiftedList = {null} & List.RemoveLastN(#"Changed Type"[Combo],1),
custom1 = Table.ToColumns(#"Changed Type") & {shiftedList},
custom2 = Table.FromColumns(custom1,Table.ColumnNames(#"Changed Type") & {"Previous Row"}),
#"Added Custom1" = Table.AddColumn(custom2, "Custom", each if [Previous Row]=null then [Index] else if [Combo]=[Previous Row] then null else [Index]),
#"Filled Down" = Table.FillDown(#"Added Custom1",{"Custom"}),
#"Grouped Rows" = Table.Group(#"Filled Down", {"Custom"}, {
{"Product", each [Product]{0} },
{"Machine", each [Machine]{0}},
{"Event Code", each [Event Code]{0}},
{"DateTime Start", each List.Min([DateTime Start]), type datetime},
{"DateTime End", each List.Max([DateTime End]), type datetime},
{"Duration (s)", each List.Sum([#"Duration (s)"]), type nullable number},
{"Rows Count", each Table.RowCount(_), Int64.Type}
}),
#"Removed Columns" = Table.RemoveColumns(#"Grouped Rows",{"Custom"})
in #"Removed Columns"

Might be faster if you don't include the Duration column in any of this then calculate it separately in a final step