mirror of
https://github.com/metrostroi-repo/MetrostroiAddon.git
synced 2026-05-02 00:42:29 +00:00
349 lines
15 KiB
Lua
349 lines
15 KiB
Lua
--------------------------------------------------------------------------------
|
|
-- Generic relay with configureable parameters
|
|
--------------------------------------------------------------------------------
|
|
-- Copyright (C) 2013-2018 Metrostroi Team & FoxWorks Aerospace s.r.o.
|
|
-- Contains proprietary code. See license.txt for additional information.
|
|
--------------------------------------------------------------------------------
|
|
Metrostroi.DefineSystem("Relay")
|
|
|
|
local relay_types = {
|
|
["PK-162"] = {
|
|
pneumatic = true,
|
|
contactor = true,
|
|
},
|
|
["Switch"] = {
|
|
contactor = true,
|
|
},
|
|
["GV_10ZH"] = {
|
|
contactor = true,
|
|
normally_closed = true,
|
|
},
|
|
["VA21-29"] = {
|
|
contactor = true,
|
|
normally_closed = true,
|
|
bass = true,
|
|
},
|
|
["AVU-045"] = {
|
|
bass = true,
|
|
},
|
|
["Switch"] = {
|
|
close_time = 0,
|
|
open_time = 0,
|
|
},
|
|
["ARS"] = {
|
|
close_time = 0,
|
|
open_time = 0,
|
|
}
|
|
}
|
|
|
|
function TRAIN_SYSTEM:Initialize(parameters,extra_parameters)
|
|
----------------------------------------------------------------------------
|
|
-- Initialize parameters
|
|
if not parameters then parameters = {} end
|
|
if type(parameters) ~= "table" then
|
|
relay_type = parameters
|
|
if relay_types[relay_type] then
|
|
parameters = relay_types[relay_type]
|
|
else
|
|
--print("[sys_relay.lua] Unknown relay type: "..parameters)
|
|
parameters = {}
|
|
end
|
|
parameters.relay_type = relay_type
|
|
end
|
|
|
|
-- Create new table
|
|
local old_param = parameters
|
|
parameters = {} for k,v in pairs(old_param) do parameters[k] = v end
|
|
|
|
-- Add extra parameters
|
|
if type(extra_parameters) == "table" then
|
|
for k,v in pairs(extra_parameters) do
|
|
parameters[k] = v
|
|
end
|
|
end
|
|
|
|
-- Contactors have different failure modes
|
|
parameters.contactor = parameters.contactor or false
|
|
-- Should the relay be initialized in 'closed' state
|
|
parameters.normally_closed = parameters.normally_closed or false
|
|
-- Time in which relay will close (seconds)
|
|
parameters.close_time = parameters.close_time or 0.050
|
|
-- Time in which relay will open (seconds)
|
|
parameters.open_time = parameters.open_time or 0.050
|
|
-- Is relay latched (stays in its position even without voltage)
|
|
parameters.latched = parameters.latched or false
|
|
-- Should relay be spring-returned to initial position
|
|
parameters.returns = parameters.returns or (not parameters.latched)
|
|
-- Trigger level for the relay
|
|
parameters.trigger_level = parameters.trigger_level or 0.5
|
|
for k,v in pairs(parameters) do
|
|
self[k] = v
|
|
end
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Relay parameters
|
|
if self.close_time == 0 then
|
|
FailSim.AddParameter(self,"CloseTime", { value = parameters.close_time})
|
|
else
|
|
FailSim.AddParameter(self,"CloseTime", { value = parameters.close_time, precision = self.contactor and 0.35 or 0.10, min = 0.010, varies = true })
|
|
end
|
|
if self.open_time == 0 then
|
|
FailSim.AddParameter(self,"OpenTime", { value = parameters.open_time})
|
|
else
|
|
FailSim.AddParameter(self,"OpenTime", { value = parameters.open_time, precision = self.contactor and 0.35 or 0.10, min = 0.010, varies = true })
|
|
end
|
|
-- Did relay short-circuit?
|
|
FailSim.AddParameter(self,"ShortCircuit", { value = 0.000, precision = 0.00 })
|
|
-- Was there a spurious trip?
|
|
FailSim.AddParameter(self,"SpuriousTrip", { value = 0.000, precision = 0.00 })
|
|
|
|
-- Calculate failure parameters
|
|
local MTBF = parameters.MTBF or 1000000 -- cycles, mean time between failures
|
|
local MFR = 1/MTBF -- cycles^-1, total failure rate
|
|
local openWeight,closeWeight
|
|
-- FIXME
|
|
openWeight = self.open_weight or 0.25
|
|
closeWeight = self.close_weight or 0.25
|
|
--[[if self.Contactor then
|
|
openWeight = 0.25
|
|
closeWeight = 0.25
|
|
elseif self.NormallyOpen then
|
|
openWeight = 0.4
|
|
closeWeight = 0.1
|
|
else
|
|
openWeight = 0.1
|
|
closeWeight = 0.4
|
|
end]]--
|
|
|
|
-- Add failure points
|
|
FailSim.AddFailurePoint(self, "CloseTime", "Mechanical problem (close time not nominal)",
|
|
{ type = "precision", value = 0.5, mfr = MFR*0.65*openWeight, recurring = true } )
|
|
FailSim.AddFailurePoint(self, "OpenTime", "Mechanical problem (open time not nominal)",
|
|
{ type = "precision", value = 0.5, mfr = MFR*0.65*closeWeight , recurring = true } )
|
|
FailSim.AddFailurePoint(self, "CloseTime", "Stuck closed",
|
|
{ type = "value", value = 1e9, mfr = MFR*0.65*openWeight, dmtbf = 0.2 } )
|
|
FailSim.AddFailurePoint(self, "OpenTime", "Stuck open",
|
|
{ type = "value", value = 1e9, mfr = MFR*0.65*closeWeight , dmtbf = 0.4 } )
|
|
FailSim.AddFailurePoint(self, "SpuriousTrip", "Spurious trip",
|
|
{ type = "on", mfr = MFR*0.20, dmtbf = 0.4 } )
|
|
--FailSim.AddFailurePoint(self, "ShortCircuit", "Short-circuit",
|
|
--{ type = "on", mfr = MFR*0.15, dmtbf = 0.2 } )
|
|
|
|
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Initial relay state
|
|
if self.normally_closed then
|
|
self.TargetValue = 1.0
|
|
self.Value = 1.0
|
|
else
|
|
self.TargetValue = self.defaultvalue or 0.0
|
|
self.Value = self.defaultvalue or 0.0
|
|
end
|
|
-- Time when relay should change its value
|
|
self.Time = 0
|
|
self.ChangeTime = nil
|
|
self.Blocked = 0
|
|
-- This increases precision at cost of perfomance
|
|
self.SubIterations = parameters.iterations or 1--relay
|
|
end
|
|
|
|
function TRAIN_SYSTEM:Inputs()
|
|
return { "Open","Close","+","-","Set","Toggle","Block","OpenBypass","Check","OpenTime","CloseTime"}
|
|
end
|
|
|
|
function TRAIN_SYSTEM:Outputs()
|
|
return { "Value" , "Blocked","TargetValue"}
|
|
end
|
|
|
|
|
|
function TRAIN_SYSTEM:TriggerInput(name,value)
|
|
-- Boolean values accepted
|
|
if type(value) == "boolean" then value = value and 1 or 0 end
|
|
if name == "OpenTime" then
|
|
self.open_time = value
|
|
FailSim.AddParameter(self,"OpenTime", { value = self.open_time})
|
|
end
|
|
if name == "CloseTime" then
|
|
self.close_time = value
|
|
FailSim.AddParameter(self,"CloseTime", { value = self.close_time})
|
|
end
|
|
if name == "Reset" then
|
|
if self.normally_closed then
|
|
self:TriggerInput("Set",1)
|
|
else
|
|
self:TriggerInput("Set",self.defaultvalue or 0.0)
|
|
end
|
|
end
|
|
--print(name)
|
|
if name == "Check" then
|
|
if value < 0 and self.Value == 1 then
|
|
self:TriggerInput("Set",0)
|
|
--self:TriggerInput("Set",0)
|
|
self.Train:PlayOnce("av_off","cabin",0.7,70)
|
|
end
|
|
return
|
|
end
|
|
if value == -1 and self.relay_type == "VA21-29" then
|
|
self:TriggerInput("Set",0)
|
|
return
|
|
end
|
|
if name == "OpenBypass" then
|
|
if (not self.ChangeTime) and (self.TargetValue ~= 0.0) then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"OpenTime")
|
|
end
|
|
self.TargetValue = 0.0
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
return
|
|
end
|
|
|
|
if self.Blocked > 0 and name ~= "Block" and (name == "Close" and self.relay_type == "PK-162" or self.relay_type ~= "PK-162") then return end
|
|
|
|
-- Open/close coils of the relay
|
|
if (name == "Block") then
|
|
self.Blocked = value
|
|
elseif (name == "Close") and (value > self.trigger_level) and (self.Value ~= 1.0 or self.TargetValue ~= 1.0) then --(self.TargetValue ~= 1.0 and self.rpb))
|
|
if self.pneumatic and self.Train.Pneumatic.TrainLinePressure < 3 then return end
|
|
if (not self.ChangeTime) or (self.TargetValue ~= 1.0) then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"CloseTime")
|
|
end
|
|
if self.Value == 1.0 then self.ChangeTime = nil end
|
|
self.TargetValue = 1.0
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
elseif (name == "Open") and (value > self.trigger_level) and (self.Value ~= 0.0 or self.TargetValue ~= 0.0) then
|
|
if (not self.ChangeTime) or (self.TargetValue ~= 0.0) then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"OpenTime")
|
|
end
|
|
self.TargetValue = 0.0
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
elseif name == "NoOpenTime" and value > 0 then
|
|
self.ChangeTime = self.Time
|
|
elseif (name == "+") and (self.Value < (self.maxvalue or self.three_position and 2 or 1)) and value > 0 then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"CloseTime")
|
|
self.TargetValue = math.min(self.maxvalue or self.three_position and 2 or 1,self.Value+1)
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
elseif (name == "-") and (self.Value > 0) and value > 0 then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"OpenTime")
|
|
self.TargetValue = math.max(0.0,self.Value-1)
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
elseif name == "Set" then
|
|
if self.pneumatic and value > 0 and self.Train.Pneumatic.TrainLinePressure < 3 then return end
|
|
if self.maxvalue then
|
|
if not self.ChangeTime then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"OpenTime")
|
|
end
|
|
self.TargetValue = math.max(0.0,math.min(self.maxvalue,math.floor(value)))
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
elseif self.three_position then
|
|
if not self.ChangeTime then
|
|
self.ChangeTime = self.Time + FailSim.Value(self,"OpenTime")
|
|
end
|
|
self.TargetValue = math.max(0.0,math.min(2.0,math.floor(value)))
|
|
if self.ChangeTime==self.Time and self.Train.DeltaTime then self:Think(self.Train.DeltaTime) end
|
|
|
|
else
|
|
if value > self.trigger_level
|
|
then self:TriggerInput("Close",self.trigger_level+1)
|
|
else self:TriggerInput("Open",self.trigger_level+1)
|
|
end
|
|
end
|
|
elseif (name == "Toggle") and (value > 0.5) then
|
|
if self.maxvalue then
|
|
self:TriggerInput("Set",self.Value > self.maxvalue-1 and 0 or self.Value+1)
|
|
elseif self.three_position then
|
|
self:TriggerInput("Set",self.Value > 1 and 0 or self.Value+1)
|
|
else
|
|
self:TriggerInput("Set",(1.0 - self.Value)*(self.trigger_level+1))
|
|
end
|
|
end
|
|
end
|
|
|
|
function TRAIN_SYSTEM:Think(dT)
|
|
--print(self.relay_type)
|
|
self.Time = self.Time + dT
|
|
-- Short-circuited relay
|
|
if FailSim.Value(self,"ShortCircuit") > 0.5 then
|
|
self.Value = 1.0
|
|
return
|
|
end
|
|
-- Spurious trip
|
|
if FailSim.Value(self,"SpuriousTrip") > 0.5 then
|
|
self.SpuriousTripTimer = self.Time + (0.5 + 2.5*math.random())
|
|
FailSim.ResetParameter(self,"SpuriousTrip",0.0)
|
|
FailSim.Age(self,1)
|
|
|
|
-- Simulate switch right away
|
|
self.Value = 1.0 - self.Value
|
|
self.TargetValue = self.Value
|
|
self.ChangeTime = nil
|
|
end
|
|
if self.SpuriousTripTimer and (self.Time > self.SpuriousTripTimer) then
|
|
self.Value = self.TargetValue
|
|
self.SpuriousTripTimer = nil
|
|
end
|
|
-- Switch relay
|
|
if self.ChangeTime and (self.Time > self.ChangeTime) and not self.SpuriousTripTimer then
|
|
-- Electropneumatic relays make this sound
|
|
if self.bass and self.Value ~= self.TargetValue then
|
|
--[[
|
|
if self.Value ~= 0.0 and self.maxvalue ~= 2 or self.Value ~= 1.0 and self.maxvalue == 2 then
|
|
if self.av3 then self.Train:PlayOnce("vu22b_on","cabin") end
|
|
if self.igla then self.Train:PlayOnce("igla_on","cabin") end
|
|
if self.button then self.Train:PlayOnce("button_press","cabin",0.51) end
|
|
if self.vud then self.Train:PlayOnce("vu22_on","cabin") end
|
|
if self.uava then self.Train:PlayOnce("uava_on","cabin") end
|
|
if self.pb then self.Train:PlayOnce("switch6","cabin") end
|
|
if self.programm then self.Train:PlayOnce("inf_on","cabin") end
|
|
if self.programm1 then self.Train:PlayOnce("triple_up-0","cabin") end
|
|
if self.programm2 then self.Train:PlayOnce("triple_down-0","cabin") end
|
|
if self.av then self.Train:PlayOnce("auto_on","cabin") end
|
|
if self.mainav then self.Train:PlayOnce("mainauto_on","cabin") end
|
|
if self.krishka then self.Train:PlayOnce("kr_close","cabin") end
|
|
if self.paketnik then self.Train:PlayOnce("pak_on","cabin") end
|
|
if self.switch then self.Train:PlayOnce("switch_on","cabin") end
|
|
if self.rcr then self.Train:PlayOnce("rcr_on","cabin") end
|
|
end
|
|
if self.Value == 0.0 and self.maxvalue ~= 2 or self.Value == 1.0 and self.maxvalue == 2 then
|
|
if self.av3 then self.Train:PlayOnce("vu22b_off","cabin") end
|
|
if self.igla then self.Train:PlayOnce("igla_off","cabin") end
|
|
if self.button then self.Train:PlayOnce("button_release","cabin",0.56) end
|
|
if self.vud then self.Train:PlayOnce("vu22_off","cabin") end
|
|
if self.uava then self.Train:PlayOnce("uava_off","cabin") end
|
|
if self.pb then self.Train:PlayOnce("switch6_off","cabin") end
|
|
if self.programm then self.Train:PlayOnce("inf_off","cabin") end
|
|
if self.programm1 then self.Train:PlayOnce("triple_0-up","cabin") end
|
|
if self.programm2 then self.Train:PlayOnce("triple_0-down","cabin") end
|
|
if self.av then self.Train:PlayOnce("auto_off","cabin") end
|
|
if self.mainav then self.Train:PlayOnce("mainauto_off","cabin") end
|
|
if self.krishka then self.Train:PlayOnce("kr_open","cabin") end
|
|
if self.paketnik then self.Train:PlayOnce("pak_off","cabin") end
|
|
if self.switch then self.Train:PlayOnce("switch_off","cabin") end
|
|
if self.rcr then self.Train:PlayOnce("rcr_off","cabin") end
|
|
end
|
|
]]
|
|
if self.bass_separate then
|
|
if self.TargetValue > 0 then
|
|
self.Train:PlayOnce(self.Name.."_on","bass",1)
|
|
else
|
|
self.Train:PlayOnce(self.Name.."_off","bass",1)
|
|
end
|
|
else
|
|
self.Train:PlayOnce(self.Name,"bass",self.TargetValue)
|
|
end
|
|
end
|
|
self.Value = self.TargetValue
|
|
self.ChangeTime = nil
|
|
|
|
-- Age relay a little
|
|
FailSim.Age(self,1)
|
|
end
|
|
end
|