--------------------------------------------------------------------------------
-- 81-722 wagon control unit
--------------------------------------------------------------------------------
-- Copyright (C) 2013-2018 Metrostroi Team & FoxWorks Aerospace s.r.o.
-- Contains proprietary code. See license.txt for additional information.
--------------------------------------------------------------------------------
Metrostroi.DefineSystem("81_722_BUKV")
TRAIN_SYSTEM.DontAccelerateSimulation = true

function TRAIN_SYSTEM:Initialize()
    self.Power = 0
    self.Commands = {}
    self.States = {}

    self.Drive = 0
    self.Brake = 0
    self.Strength = 0
    self.CurTime = CurTime()

    self.DisableLights = 0
    self.EnableLights = 0
    self.VentMode = 0
    self.DisablePSN = 0
    self.DisableTP = 0
    self.EnableMK = 0
    self.DisablePant = 0
end

function TRAIN_SYSTEM:Outputs()
    return {"Power","Drive","Brake","Strength","DisableLights","EnableLights","DisablePSN","EnableMK","DisableTP","DisablePant","VentMode"}
end

function TRAIN_SYSTEM:CState(name,value)
    if self.CurrentBUP and  (self.Reset or self.States[name] ~= value) then
        self.States[name] = value
        self.Train:CANWrite("BUKV",self.TrainIndex,"BUKP",self.CurrentBUP or false,name,value)
    end
end
function TRAIN_SYSTEM:CANReceive(source,sourceid,target,targetid,textdata,numdata)
    if textdata == "Init" and not numdata then
        local Train = self.Train
        self.Commands[sourceid] = {}
        Train:CANWrite("BUKV",self.TrainIndex,"BUKP",sourceid,"Init",{
            front=Train.FrontTrain and Train.FrontTrain.BUKV and Train.FrontTrain.BUKV.TrainIndex,
            rear=Train.RearTrain and Train.RearTrain.BUKV and Train.RearTrain.BUKV.TrainIndex,
            type=Train.Electric.Type
        })
    elseif textdata == "Get" then
        self.Reset = CurTime()
    elseif textdata == "Orientate" then
        self.OrientateBUP = sourceid
        self.FirstHalf = numdata
        self.FirstGroup = numdata
        self.Reset = CurTime()
    elseif self.CurrentBUP then
        if not self.Commands[sourceid] then self.Commands[sourceid] = {} end
        self.Commands[sourceid][textdata] = numdata
    end
end
function TRAIN_SYSTEM:Get(id)
    local Commands = self.Commands[self.CurrentBUP]
    if Commands then
        return Commands[id]
    end
end

function TRAIN_SYSTEM:Inputs()
    return {}
end

function TRAIN_SYSTEM:TriggerInput(name,value)
end
function TRAIN_SYSTEM:Think()
    if CurTime()-self.CurTime < 0.1 then return end
    self.CurTime = CurTime()

    local Train = self.Train
    local wagcount = #Train.WagonList
    self.Power = Train.Electric.Power
    self.State = self.Power > 0 and Train.SF51.Value > 0.5
    self.TrainIndex = Train:GetWagonNumber()
    if self.State then
        if not self.States.BUKVTimer then
            self.Train:CANWrite("BUKV",Train:GetWagonNumber(),"BUKP",nil,"Get",1)
        end
        --[[ for i=1,4 do
            self:CState("Door"..i.."Closed", Train.Pneumatic.LeftDoorState[i] == 0)
            self:CState("Door"..(i+4).."Closed", Train.Pneumatic.RightDoorState[i] == 0)
        end--]]
        --self:CState("DoorTorec", Train.RearDoor or Train.FrontDoor)
        --self:CState("DoorBack", Train.PassengerDoor or Train.CabinDoorLeft or Train.CabinDoorRight)
        --self:CState("EmPT",Train:ReadTrainWire(28) > 0)
        --self:CState("NoAssembly", Train.K3_4.Value == 0)
        --self:CState("ParkingBrakeEnabled", Train.Pneumatic.ParkingBrakePressure < 2.95)
        --self:CState("BEPPBroken", false)
        -- self:CState("EmergencyBrakeGood", Train.Pneumatic.BrakeCylinderPressure >= (2.4+Train.Pneumatic.WeightLoadRatio*0.9)-0.1)
        -- self:CState("ReserveChannelBraking", Train:ReadTrainWire(28)>0)
        -- self:CState("PTEnabled", Train.Pneumatic.BrakeCylinderPressure > 0.2)
        -- self:CState("PTBad", false)
        -- self:CState("PTReady", Train.Pneumatic.AirDistributorPressure >= (2.4+Train.Pneumatic.WeightLoadRatio*0.9)-0.1)
        -- self:CState("PTReplace", self.PTReplace and CurTime()-self.PTReplace > 1.5)
        -- self:CState("BTBReady", Train.Pneumatic.BTBReady)
        --self:CState("TLPressure", math.Round(Train.Pneumatic.TrainLinePressure,1))
        --self:CState("BLPressure", math.Round(Train.Pneumatic.BrakeLinePressure,1))
        --self:CState("BCPressure", math.Round(Train.Pneumatic.BrakeCylinderPressure,1))
        --self:CState("HPPressure", math.Round(Train.Pneumatic.AirDistributorPressure,1))
        --self:CState("WeightLoad", math.Round(Train.Pneumatic.WeightLoadRatio,2))
        -- self:CState("PantDisabled", self.Pant)
        -- self:CState("EnginesBroken", false)
        -- self:CState("BBEEnabled", self.BBE)
        -- self:CState("BBEBroken", false)
        -- self:CState("HVBad", Train.Electric.Power750V < 550)
        -- self:CState("LVBad", Train.Electric.Battery80V < 62)
        -- self:CState("EnginesDone", self.EnginesDone)-- and math.abs(Train.Speed) < 7.5)
        -- self:CState("EnginesBrakeBroke", (self:Get("Brake") or 0) > 0 and Train.BV.Value == 0 or Train.Electric.Brake > 0 and (Train.K3_4.Value*Train.K5_6.Value == 0))
        -- self:CState("PassLightEnabled", self.MainLights)
        -- self:CState("BVEnabled", Train.BV.Value > 0)
        -- self:CState("DriveStrength", math.min(0,Train.Engines.BogeyMoment*2))
        -- self:CState("BrakeStrength", math.max(0,Train.Engines.BogeyMoment*2))
        -- self:CState("VagEqConsumption", 15)--15-25
        --self:CState("LVVoltage", math.floor(Train.Electric.Battery80V))
        --self:CState("MKVoltage", math.Round(Train.Electric.BVKA_KM1*math.Rand(9,13),1))
        -- self:CState("Vent1Enabled", Train.Electric.Vent1>0)
        -- self:CState("Vent2Enabled", Train.Electric.Vent2>0)
        -- self:CState("HeatEnabled", false)
        self:CState("WagNOrientated", self.Orientation  == self.RevOrientation)
        self:CState("Orientation", self.Orientation)
        self:CState("BadCombination", (Train:ReadTrainWire(3)*Train:ReadTrainWire(4)) > 0)

        local BUFTPower = Train.Electric.BUFT > 0
        local Pneumatic = Train.Pneumatic
        self:CState("BLPressure", BUFTPower and math.Round(Pneumatic.BrakeLinePressure,1) or 0)
        self:CState("TLPressure", BUFTPower and math.Round(Pneumatic.TrainLinePressure,1) or 0)
        self:CState("BCPressure", BUFTPower and math.Round(Pneumatic.BrakeCylinderPressure,1) or 0)
        self:CState("SKPressure", BUFTPower and math.Round(Pneumatic.AirDistributorPressure,1) or 0)
        self:CState("PBPressure", BUFTPower and math.Round(Pneumatic.ParkingBrakePressure,1) or 0)
        self:CState("WeightLoad", BUFTPower and math.Round(Pneumatic.WeightLoadRatio,2) or 0)
        self:CState("EmergencyBrake",BUFTPower and Pneumatic.EmergencyBrakeActive)
        local closed = true
        for i=1,4 do
            self:CState("Door"..i.."Closed", Train.Pneumatic.LeftDoorState[i] == 0)
            self:CState("Door"..(i+4).."Closed", Train.Pneumatic.RightDoorState[i] == 0)
            if Train.Pneumatic.LeftDoorState[i] > 0 or Train.Pneumatic.RightDoorState[i] > 0 then closed = false end
        end
        self:CState("DoorsClosed", closed)
        self:CState("TPEnabled", true)--FIXME
        self:CState("TRear", Train.RearDoor)
        if Train.Electric.HaveBUKP==0 then
            self:CState("TFront", Train.FrontDoor)
        else
            self:CState("TFront", Train.PassengerDoor)
            self:CState("TLeft", Train.CabinDoorLeft)
            self:CState("TRight", Train.CabinDoorRight)
        end
        self:CState("DPBD1", Train.Panel.BrW>0)
        self:CState("LVVoltage", Train.Electric.Power*65)
        if Train.Electric.HaveAsyncInverter>0 then
            self:CState("MKWork", Train.Pneumatic.Compressor)
            self:CState("PSNWork", Train.Electric.PSN>0)

            local Electric = Train.Electric
            local AsyncInverter = Train.AsyncInverter
            self:CState("HVVoltage", math.floor(Electric.Main750V))
            self:CState("Current", math.Round(AsyncInverter.Current,1))
            self:CState("DTorque", math.max(0,math.Round(AsyncInverter.Torque,1)))
            self:CState("BTorque", math.max(0,-math.Round(AsyncInverter.Torque,1)))

            self:CState("AsyncOverheat", false) --FIXME
            self:CState("AsyncAssembly", AsyncInverter.Mode~=0 and (AsyncInverter.Mode==self.Drive*(1-self.Brake) or AsyncInverter.Mode*(-1)==self.Brake*(1-self.Drive)))
            self:CState("AsyncFail", AsyncInverter.Mode==0 and (self.Drive>0 and self.Brake>0) or AsyncInverter.Power == 0)
            self:CState("AsyncEFail", (AsyncInverter.Mode==0 or AsyncInverter.EDone > 0) and self.Brake>0) --FIXME
            --self:CState("AsyncBroken",  AsyncInverter.Power == 0) --FIXME
            self:CState("AsyncProtection",  AsyncInverter.Power == 0) --FIXME
            self:CState("BVState", not self:Get("PVU5")) --FIXME
            self:CState("AsyncEDone", AsyncInverter.EDone>0 or AsyncInverter.Mode==-1 and Train.Speed < 3.5) --FIXME

            self:CState("NoHV", Electric.Main750V < 550)

            local TR = Train.TR
            self:CState("PantDisabled", TR.ContactState1==0 and TR.ContactState2==0 and TR.ContactState3==0 and TR.ContactState4==0)
            self:CState("DisablePant", self.DisablePant>0)
        else
            local speed = Train.Speed
            self.EDone = self.Brake*((speed<=5 or speed<=10 and (self.Mode==0 or self.EDone > 0)) and 1 or 0)
            self.Mode = self.Brake
            self:CState("AsyncEDone", self.EDone>0) --FIXME
        end
        self:CState("LightsEnabled", Train.Panel.MainLights>0) --FIXME
        self:CState("Vent1Enabled", Train.Electric.Vent1>0)
        self:CState("Vent2Enabled", Train.Electric.Vent2>0)
        --? Сигнал датчика  противозажатия дверей
        --! Сигнал датчика положения токоприёмников
        --? Сигнал датчика состояния замка торцевой двери
        --? Сигнал датчика исправности мотор-компрессора
        --! Сигнал датчика  положения блок-тормоза 5 – 8
        --? Сигнал датчика перегрева букс 1 – 4
        --? Сигнал датчика перегрева букс 5 – 8
        --? Ток потребления МК
        --? Ток потребления ВО
        --? Сигнал датчика замыкания 75 В на корпус
        --? Состояние петли безопасности
        --? Неисправность вентилятора тормозного реостата

        for i=1,5 do
            self:CState("PVU"..i, self:Get("PVU"..i))
        end
        if self:Get("PVU6") then
            self.AsyncDisabled = true
        elseif self.AsyncDisabled == true then
            self.AsyncDisabled = CurTime()
        elseif self.AsyncDisabled and CurTime()-self.AsyncDisabled > 1.5 then
            self.AsyncDisabled = false
        end
        self:CState("PVU6", not not self.AsyncDisabled)
        self:CState("PVU7", self:Get("PVU7"))
        self:CState("BUKVTimer", math.floor((CurTime()*2)%10))
    elseif self.States.BUKVTimer then
        for k,v in pairs(self.Commands) do
            self.Commands[k] = false
        end
        for k,v in pairs(self.States) do
            self.States[k] = false
        end
    end

    if self.Reset and self.Reset ~= CurTime() then
        self.Reset = nil
    end

    if self.Emer ~= Train.Electric.Emer then
        self.Emer = Train.Electric.Emer
        if self.Emer > 0 then
            for i=1,7 do
                self.States["PVU"..i] = false
            end
        end
    end

    local PTReplace = self.States.AsyncEFail
    if PTReplace and not self.PTReplace and Train.Electric.HaveAsyncInverter>0 then
        self.PTReplace =  CurTime()
        if self.States.AsyncProtection == 0 or self:Get("PVU5")  or self:Get("PVU6")  then
            self.PTReplace = self.PTReplace - 1.2
        elseif self.States.AsyncAssembly then
            self.PTReplace = self.PTReplace + 1.3
        end
    end
    if not PTReplace and self.PTReplace then self.PTReplace = nil end
    local PN =  self.PTReplace and CurTime()-self.PTReplace > 1.2 or self.States.AsyncEDone
    self.PN1 = self:Get("PN1") or PN and (self:Get("DriveStrength") and self:Get("DriveStrength") > 0) or self:Get("ARSBrake") and (Train.Electric.Type>1 or not self.States.AsyncAssembly)
    self.PN2 = self:Get("PN2") or PN and (self:Get("DriveStrength") and (not self.States.AsyncEDone and not PTReplace and self:Get("DriveStrength") > 0.2 or (PTReplace or self.States.AsyncEDone) and self:Get("DriveStrength") > 0.7 or self:Get("BrakeTPlus")))

    --self.MK = (not self:Get("PVU3") and self:Get("Compressor"))and 1 or 0

    self.OpenLeft = not self:Get("PVU1") and (self:Get("OpenLeft") and self.Orientation or self:Get("OpenRight") and not self.Orientation)
    self.OpenRight = not self:Get("PVU1") and (self:Get("OpenRight") and self.Orientation or self:Get("OpenLeft") and not self.Orientation)
    self.OpenRightBack = self:Get("OpenRightBack") and Train.Electric.HaveBUKP>0
    self.CloseDoors = self:Get("PVU1") or self:Get("CloseDoors")
    local strength,brake = self:Get("DriveStrength") or 0,self:Get("Brake") and 1 or 0
    self.Drive = (1-brake)*(strength>0 and 1 or 0)
    self.Brake = brake*(strength>0 and 1 or 0)
    local BCPressure = self.States.BCPressure or 0
    if self.Brake>0 and BCPressure < 1.5 or self.Drive>0--[[ and BCPressure < 0.5]] then
        self.Strength = strength*100
    else
        self.Strength = 0
    end
    local clRing = self:Get("CloseRing")
    if self.CloseRing ~= clRing then
        self.CloseRing = clRing
        if clRing then Train:PlayOnce("door_alarm",1,1) end
    end
    self.DisableLights = self:Get("PVU2") and 1 or 0
    self.EnableLights = self:Get("PassLight") and 1 or 0
    self.DisablePSN = self:Get("PVU3") and 1 or 0
    self.EnableMK = (not self:Get("PVU4") and self:Get("Compressor")) and 1 or 0
    self.DisableTP = (self:Get("PVU5") or self:Get("PVU6")) and 1 or 0
    self.DisablePant = (self:Get("DisablePant") or self:Get("PVU7")) and 1 or 0
    self.ParkingBrake = self:Get("ParkingBrake")
    local vent = self:Get("PassVent") or 0
    if vent==1 then
        self.VentMode = Train.Pneumatic.WeightLoadRatio>0.6 and 2 or Train.Pneumatic.WeightLoadRatio>0.1 and 1 or 0
    elseif vent>1 then
        self.VentMode = vent-1
    else
        self.VentMode = vent
    end

    self.Orientation = Train:ReadTrainWire(4) > 0
    self.RevOrientation = Train:ReadTrainWire(3) > 0
    local BadOrientation = false
    if self.Orientation and self.Orientation  == self.RevOrientation then
        self.Orientation = false
        self.RevOrientation = false
        BadOrientation = true
    end
    if self.State and self.Orientation ~= self.RevOrientation then
        if not self.BadOrientation and self.OrientateBUP and (not self.Commands[self.OrientateBUP] or self.Orientation and self.Commands.Forward ~= self.OrientateBUP or self.RevOrientation and self.Commands.Back ~= self.OrientateBUP) then
            if self.Orientation then self.Commands.Forward = self.OrientateBUP else self.Commands.Back = self.OrientateBUP end
            self.OrientateBUP = nil
        elseif BadOrientation then
            self.OrientateBUP = nil
        end
    elseif not self.State then self.OrientateBUP = nil end
    local ReOrientation = self.State and (self.Orientation or self.RevOrientation) and (self.Orientation ~= self.PrevOrientation or self.RevOrientation ~= self.PrevRevOrientation or self.CurrentBUP ~= (self.Orientation and self.Commands.Forward or self.Commands.Back))
    if ReOrientation then
        self.CurrentBUP = self.Orientation and self.Commands.Forward or self.Commands.Back
        self.Reset = CurTime()
        if self.CurrentBUP then
            self.Commands[self.CurrentBUP]  = {}
            Train:CANWrite("BUKV",Train:GetWagonNumber(),"BUKP",self.CurrentBUP,"Get")
        end
    end
    self.BadOrientation = BadOrientation
    self.PrevOrientation = self.State and self.Train:ReadTrainWire(4) > 0
    self.PrevRevOrientation = self.State and self.Train:ReadTrainWire(3) > 0
end